## Automatically generated incremental diff
## From:   linux-2.2.18-pre15
## To:     linux-2.2.18-pre16
## Robot:  $Id: make-incremental-diff,v 1.5 2001/12/01 08:32:36 hpa Exp $

diff -urN linux-2.2.18-pre15/Documentation/Changes linux/Documentation/Changes
--- linux-2.2.18-pre15/Documentation/Changes	Mon Sep  4 10:39:15 2000
+++ linux/Documentation/Changes	Mon Dec  3 20:33:54 2001
@@ -224,13 +224,14 @@
 your system, you don't have to upgrade just so the kernel will work
 (though feel free to upgrade if you want the gcc bug fixes).
 
-   Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad
+   Note that the latest compilers (pgcc, gcc 2.95) may do Bad
 Things while compiling your kernel, particularly if absurd
-optimizations (like -O9) are used.  Caveat emptor.  Currently, the only
-C compiler available in a binary distribution is egcs.  Version 1.1.2
-seems okay; if you have to have a binary, you may be successful using
-that.  In general, however, gcc-2.7.2.3 is known to be stable, while
-egcs and others have not been as thoroughly tested yet.
+optimizations (like -O9) are used.  Caveat emptor. In general, however, 
+gcc-2.7.2.3 and egcs 1.1.2 are known to be stable on x86, while gcc 2.95 and 
+others have not been as thoroughly tested yet.
+
+For non x86 platforms consult the platform specific information for
+recommended compilers.
 
 Networking Changes
 ==================
@@ -651,9 +652,9 @@
 ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 
-The kernel-level 1.4.7 release:
-ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz
-ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz
+
+The kernel-level nfs-utils-0.1.6 release:
+ftp://nfs.sourceforge.net/pub/nfs/nfs-utils-0.1.6.tar.gz
 
 Net-tools
 =========
diff -urN linux-2.2.18-pre15/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.2.18-pre15/Documentation/Configure.help	Mon Dec  3 20:33:28 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 20:33:54 2001
@@ -693,6 +693,50 @@
   I/O speeds to be set as well. See the files Documentation/ide.txt
   and drivers/block/ali14xx.c for more info.
 
+Generic PC I/O port IDE interface support
+CONFIG_BLK_DEV_PCIDE
+  This is the IDE driver for most generic PC style IDE interfaces.
+  Say Y if you have a PC and want to use IDE devices (hard disks, CD-ROM
+  drives, etc.) that are connected to the builtin IDE interface.
+
+Amiga builtin Gayle IDE interface support
+CONFIG_BLK_DEV_GAYLE
+  This is the IDE driver for the builtin IDE interface on some Amiga
+  models.  It supports both the `A1200 style' (used in A600 and A1200)
+  and `A4000 style' (used in A4000 and A4000T) of the Gayle IDE
+  interface.
+  Say Y if you have such an Amiga model and want to use IDE devices
+  (hard disks, CD-ROM drives, etc.) that are connected to the builtin
+  IDE interface.
+
+Falcon IDE interface support
+CONFIG_BLK_DEV_FALCON_IDE
+  This is the IDE driver for the builtin IDE interface on the Atari
+  Falcon.
+  Say Y if you have a Falcon and want to use IDE devices (hard disks,
+  CD-ROM drives, etc.) that are connected to the builtin IDE interface.
+
+Amiga Buddha/Catweasel IDE interface support (EXPERIMENTAL)
+CONFIG_BLK_DEV_BUDDHA
+  This is the IDE driver for the IDE interfaces on the Buddha and
+  Catweasel expansion boards.  It supports up to two interfaces on the
+  Buddha and three on the Catweasel.
+  Say Y if you have a Buddha or Catweasel expansion board and want to
+  use IDE devices (hard disks, CD-ROM drives, etc.) that are connected
+  to one of its IDE interfaces.
+
+Amiga IDE Doubler support (EXPERIMENTAL)
+CONFIG_BLK_DEV_IDEDOUBLER
+  This driver provides support for the so called `IDE doublers' (made by
+  various manufacturers, e.g. Eyetech) that can be connected to the
+  builtin IDE interface of some Amiga models. Using such an IDE doubler,
+  you can connect up to four instead of two IDE devices on the Amiga's
+  builtin IDE interface.
+  Note that the normal Amiga Gayle IDE driver may not work correctly if
+  you have an IDE doubler and don't enable this driver!
+  Say Y if you have an IDE doubler.  The driver is enabled at kernel
+  runtime using the "ide=doubler" kernel boot parameter.
+
 XT hard disk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT computer
@@ -1913,7 +1957,8 @@
 PowerMac "valkyrie" frame buffer device support
 CONFIG_FB_VALKYRIE
   This driver supports a frame buffer for the "valkyrie" graphics
-  adapter in some Power Macintoshes.
+  adapter found in some Power Macintoshes, as well as the 580 and 630
+  series 68k Macintoshes.
 
 Chips 65550 display support
 CONFIG_FB_CT65550
@@ -1922,8 +1967,9 @@
 
 Mac frame buffer device
 CONFIG_FB_MAC
-  This is the frame buffer device driver for the graphics hardware in
-  m68k Macintoshes.
+  This is the a generic frame buffer device driver for the graphics
+  hardware in m68k Macintoshes.  It can only use the video mode set
+  by MacOS at boot time.  You should probably say Y here.
 
 HP300 frame buffer device
 CONFIG_FB_HP300
@@ -5850,6 +5896,20 @@
   If you want to compile this as a module, say M and read
   Documentation/modules.txt. The module will be called comx-proto-fr.o.
 
+Xpeed DSL NIC support
+CONFIG_XPEED
+  This driver supports Xpeed Inc. PCI network cards.
+  The following cards are supported with this driver:
+
+  Xpeed X200 IDSL NIC (http://www.xpeed.com/Products/x200/x200_c.html)
+  Xpeed X300 SDSL NIC (http://www.xpeed.com/Products/x300/x300_c.html)
+
+  This driver handles frame relay encapsulation of WAN link and presents
+  the card to the kernel as an ethernet-like device called dsl0, dsl1, etc.
+
+  Currently the driver only functions as a module. Detailed configuration
+  information can be found in Documentation/networking/README.xpeed
+
 Generic HDLC driver
 CONFIG_HDLC
   Say Y to this option if your Linux box contains a WAN card supported
@@ -7954,6 +8014,18 @@
 
   Most users want to say Y here.
 
+Support for hot-pluggable USB devices
+CONFIG_HOTPLUG
+ Say Y here if you want to plug devices into your computer while
+ the system is running, and be able to use them quickly.  In many
+ cases, the devices can likewise be unplugged at any time too.
+
+ Enable this with KMOD, and your kernel will automatically
+ call out to a user mode "policy agent" to load drivers and other
+ modules needed to use USB devices you plug in.  With a bit of work,
+ it can invoke other device setup tasks.  Get such agent software
+ (at http://www.linux-usb.org/policy.html) and install it.
+
 USB Bandwidth allocation
 CONFIG_USB_BANDWIDTH
   If you say Y here, the USB subsystem enforces USB bandwidth
@@ -8328,31 +8400,33 @@
 
 NFS server support
 CONFIG_NFSD
-  If you want your Linux box to act as a NFS *server*, so that other
+  If you want your Linux box to act as an NFS *server*, so that other
   computers on your local network which support NFS can access certain
   directories on your box transparently, you have two options: you can
   use the self-contained user space program nfsd, in which case you
-  should say N here, or you can say Y and use this new experimental
-  kernel based NFS server. The advantage of the kernel based solution
-  is that it is faster; it might not be completely stable yet, though.
+  should say N here, or you can say Y and use the kernel based NFS
+  server. The kernel based solution is faster and is now the recommended
+  solution: no further development is occurring on the userspace server and
+  support of it may be discontinued in future.
 
   In either case, you will need support software; the respective
   locations are given in the file Documentation/Changes in the NFS
   section.
 
-  Please read the NFS-HOWTO, available via FTP (user: anonymous) from
-  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
+  Please read the NFS-HOWTO, available from
+  http://www.linuxdoc.org/HOWTO/NFS-HOWTO.html .
+
 
   The NFS server is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module is called nfsd.o. If you want to compile it as a module,
   say M here and read Documentation/modules.txt. If unsure, say N.
 
-Emulate Sun NFS daemon
-CONFIG_NFSD_SUN
-  If you would like for the server to allow clients to access
-  directories that are mount points on the local filesystem (this is
-  how nfsd behaves on Sun systems), say yes here. If unsure, say N.
+Provide NFSv3 server support (EXPERIMENTAL)
+CONFIG_NFSD_V3
+  If you would like to include the NFSv3 server as well as the NFSv2
+  server, say Y here.  File locking, via the NLMv4 protocol, is now
+  supported. If unsure, say N.
 
 OS/2 HPFS filesystem support (read only)
 CONFIG_HPFS_FS
@@ -11894,6 +11968,32 @@
   If you anticipate running this kernel on a computer with a MC68060
   processor, say Y. Otherwise, say N.
 
+Math emulation support
+CONFIG_FPU_EMU
+  At some point in the future, this will cause floating-point math
+  instructions to be emulated by the kernel on machines that lack a
+  floating-point math coprocessor.  Thrill-seekers and chronically
+  sleep-deprived psychotic hacker types can say Y now, everyone else
+  should probably wait a while.
+
+Math emulation only kernel
+CONFIG_FPU_EMU_ONLY
+  This option prevents any floating-point instructions from being
+  compiled into the kernel, thereby the kernel doesn't save any
+  floating point context anymore during task switches, so this
+  kernel will only be usable on machines without a floating-point
+  math coprocessor. This makes the kernel a bit faster as no tests
+  needs to be executed whether a floating-point instruction in the
+  kernel should be executed or not.
+
+Math emulation extra precision
+CONFIG_FPU_EMU_EXTRAPREC
+  The fpu uses normally a few bit more during calculations for
+  correct rounding, the emulator can (often) do the same but this
+  extra calculation can cost quite some time, so you can disable
+  it here. The emulator will then only calculate with a 64 bit
+  mantissa and round slightly incorrect.
+
 Advanced processor options
 CONFIG_ADVANCED_CPU
   This gives you access to some advanced options for the CPU. The
@@ -12096,6 +12196,21 @@
   has only been tested on a Hades with a 68060 processor. Before you
   use this, make backups of your entire hard disk.
 
+Macintosh NCR5380 (II-series) SCSI
+CONFIG_MAC_SCSI
+  If you have a Macintosh with NCR5380-based SCSI, say Y.  Otherwise,
+  say N.  This SCSI adaptor is found on all 68030-based Macs,
+  including the II, IIx, IIcx, IIci, IIsi, IIvx, IIvi, Color Classic,
+  Classic II, LC II, LC III, and their associated Performa models, as
+  well as the 68030-based PowerBooks and Duos.
+
+Macintosh NCR53c9[46] (Quadra/Centris) SCSI
+CONFIG_SCSI_MAC_ESP
+  If you have a Macintosh with NCR53c96 or NCR53c94-based SCSI, say
+  Y.  Otherwise, say N.  These SCSI adaptors are found on all
+  68040-based Macs, including all Quadra and Centris models, the LC
+  475/476 and 575, and the Performa 575, 580, and 630.
+
 Ariadne support
 CONFIG_ARIADNE
   If you have a Village Tronic Ariadne Ethernet adapter, say Y.
@@ -12163,6 +12278,30 @@
   ACSI port ("ACSI node"). The driver works (has to work...) with a
   polled I/O scheme, so it's rather slow :-(
 
+Macintosh NS 8390 based ethernet cards
+CONFIG_DAYNAPORT
+  Say Y to include support for Macintosh ethernet adaptors based on
+  the National Semiconductor 8390 and ST-NIC chips.  This family of
+  cards includes the majority of NuBus ethernet cards, as well as many
+  LC-PDS slot cards, from Apple, Asante, Cabletron, Farallon, and
+  other manufacturers.
+
+Macintosh SONIC based ethernet
+CONFIG_MACSONIC
+  Say Y to include support for Macintosh ethernet adaptors based on
+  the National Semiconductor SONIC chip (83932 and 83934).  If you
+  have a Quadra with onboard SONIC ethernet, say Y here.  Onboard
+  SONIC ethernet chips are found on all Quadra 605, 610, 650, 700,
+  800, 900, and 950 models, all Centris 650, most Centris 610, and all
+  LC475/476 models.  It is also found in the DuoDock Plus and DuoDock
+  II, as well as many NuBus, LC-PDS, and comm-slot cards. 
+
+Macintosh (AV) onboard MACE ethernet
+CONFIG_MACMACE
+  Say Y here if you have a Centris 660AV, a Quadra 660AV, or a Quadra
+  840AV, and you would like to use the onboard MACE Ethernet adaptor.
+
+
 Multiface Card III parallel support
 CONFIG_MULTIFACE_III_LP
   If you have a Multiface III card for your Amiga, and want to use its
@@ -12277,6 +12416,11 @@
   inserted in and removed from the running kernel whenever you want).
   If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
+
+Macintosh SCC serial support
+CONFIG_MAC_SCC
+  If you have a Macintosh and would like to use its serial
+  ("Printer" and "Modem") ports in Linux, say Y.  Otherwise, say N.
 
 Amiga or Atari DMA sound support
 CONFIG_DMASOUND
diff -urN linux-2.2.18-pre15/Documentation/IO-mapping.txt linux/Documentation/IO-mapping.txt
--- linux-2.2.18-pre15/Documentation/IO-mapping.txt	Wed Jun 24 14:30:07 1998
+++ linux/Documentation/IO-mapping.txt	Mon Dec  3 20:33:54 2001
@@ -176,7 +176,7 @@
 didn't think straight when I wrote it originally.  People who have to
 support both can do something like:
  
-	/* support old naming sillyness */
+	/* support old naming silliness */
 	#if LINUX_VERSION_CODE < 0x020100                                     
 	#define ioremap vremap
 	#define iounmap vfree                                                     
diff -urN linux-2.2.18-pre15/Documentation/SubmittingDrivers linux/Documentation/SubmittingDrivers
--- linux-2.2.18-pre15/Documentation/SubmittingDrivers	Mon Dec  3 20:33:28 2001
+++ linux/Documentation/SubmittingDrivers	Mon Dec  3 20:33:54 2001
@@ -41,11 +41,11 @@
 What Criteria Determine Acceptance
 ----------------------------------
 
-Licensing:	The code must be released to us under the GNU public license. 
-		We don't insist on any kind of exclusively GPL licensing,
-		and if you wish the driver to be useful to other communities
-		such as BSD you may well wish to release under multiple
-		licenses.
+Licensing:	The code must be released to us under the GNU General Public
+		License. We don't insist on any kind of exclusively GPL 
+		licensing, and if you wish the driver to be useful to other 
+		communities such as BSD you may well wish to release under 
+		multiple licenses.
 
 Interfaces:	If your driver uses existing interfaces and behaves like
 		other drivers in the same class it will be much more likely
diff -urN linux-2.2.18-pre15/Documentation/arm/nwfpe/README linux/Documentation/arm/nwfpe/README
--- linux-2.2.18-pre15/Documentation/arm/nwfpe/README	Sun May  2 09:51:16 1999
+++ linux/Documentation/arm/nwfpe/README	Mon Dec  3 20:33:54 2001
@@ -15,7 +15,7 @@
 important.
 
 Another choice I made was in the file structure.  I have attempted to
-contain all operating system specfic code in one module (fpmodule.*).
+contain all operating system specific code in one module (fpmodule.*).
 All the other files contain emulator specific code.  This should allow
 others to port the emulator to NetBSD for instance relatively easily.
 
diff -urN linux-2.2.18-pre15/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- linux-2.2.18-pre15/Documentation/cdrom/cdrom-standard.tex	Mon Dec  3 20:33:28 2001
+++ linux/Documentation/cdrom/cdrom-standard.tex	Mon Dec  3 20:33:54 2001
@@ -919,7 +919,7 @@
   than fixing this interface by changing the assumptions it was made
   under, thereby breaking all user applications that use this
   function, the \UCD\ implements this $ioctl$ as follows: If the CD in
-  question has audio tracks on it, and it has absolutly no CD-I, XA,
+  question has audio tracks on it, and it has absolutely no CD-I, XA,
   or data tracks on it, it will be reported as $CDS_AUDIO$.  If it has
   both audio and data tracks, it will return $CDS_MIXED$.  If there
   are no audio tracks on the disc, and if the CD in question has any
diff -urN linux-2.2.18-pre15/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd
--- linux-2.2.18-pre15/Documentation/cdrom/ide-cd	Mon Dec  3 20:33:28 2001
+++ linux/Documentation/cdrom/ide-cd	Mon Dec  3 20:33:54 2001
@@ -286,7 +286,7 @@
     Unfortunately, these drives seem to become very confused when we perform
     the standard Linux ATA disk drive probe. If you own one of these drives,
     you can bypass the ATA probing which confuses these CDROM drives, by 
-    adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and runing 
+    adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and running 
     lilo (again where X is the drive letter corresponding to where your drive 
     is installed.)
     
diff -urN linux-2.2.18-pre15/Documentation/computone.txt linux/Documentation/computone.txt
--- linux-2.2.18-pre15/Documentation/computone.txt	Wed Jun  7 14:26:42 2000
+++ linux/Documentation/computone.txt	Mon Dec  3 20:33:54 2001
@@ -93,7 +93,7 @@
 to update the character drivers' makefile and configuration file, and other 
 kernel source files. A build script (ip2build) was included which applies 
 the patches if needed, and build any utilities needed.
-What you recieve may be a single patch file in conventional kernel
+What you receive may be a single patch file in conventional kernel
 patch format build script. That form can also be applied by
 running patch -p1 < ThePatchFile.  Otherwise run ip2build.
  
@@ -191,7 +191,7 @@
 Intelliport II installations using the PowerPort expansion module can
 use the custom speed setting to select the highest speeds: 153,600 bps,
 230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for
-custom baud rate configuration is fixed at 921,600 for cards/expantion
+custom baud rate configuration is fixed at 921,600 for cards/expansion
 modules with ST654's and 115200 for those with Cirrus CD1400's.  This
 corresponds to the maximum bit rates those chips are capable.  
 For example if the baud base is 921600 and the baud divisor is 18 then
@@ -220,13 +220,13 @@
 2.2.x kernels and available as a configuration option in 2.3.46 and higher.
 Devfs allows for the automatic creation and management of device names
 under control of the device drivers themselves.  The Devfs namespace is
-hierarchial and reduces the clutter present in the normal flat /dev
+hierarchical and reduces the clutter present in the normal flat /dev
 namespace.  Devfs names and conventional device names may be intermixed.
 A userspace daemon, devfsd, exists to allow for automatic creation and
 management of symbolic links from the devfs name space to the conventional
 names.  More details on devfs can be found on the DEVFS home site at
 <http://www.atnf.csiro.au/~rgooch/linux/> or in the file kernel
-documenation files, .../linux/Documenation/filesystems/devfs/REAME.
+documentation files, .../linux/Documentation/filesystems/devfs/REAME.
 
 If you are using devfs, existing devices are automatically created within
 the devfs name space.  Normal devices will be ttf/0 - ttf/255 and callout
diff -urN linux-2.2.18-pre15/Documentation/cpqarray.txt linux/Documentation/cpqarray.txt
--- linux-2.2.18-pre15/Documentation/cpqarray.txt	Mon Sep  4 10:39:16 2000
+++ linux/Documentation/cpqarray.txt	Mon Dec  3 20:33:54 2001
@@ -1,4 +1,4 @@
-This driver is for Compaq's SMART2 Intellegent Disk Array Controllers.
+This driver is for Compaq's SMART2 Intelligent Disk Array Controllers.
 
 Supported Cards:
 ----------------
diff -urN linux-2.2.18-pre15/Documentation/fb/vesafb.txt linux/Documentation/fb/vesafb.txt
--- linux-2.2.18-pre15/Documentation/fb/vesafb.txt	Thu Apr 29 11:53:41 1999
+++ linux/Documentation/fb/vesafb.txt	Mon Dec  3 20:33:54 2001
@@ -62,7 +62,7 @@
 16M |  0x312    0x315    0x318    0x31B   
 
 To enable one of those modes you have to specify "vga=ask" in the
-lilo.conf file and rerun LILO. Then you can type in the descired
+lilo.conf file and rerun LILO. Then you can type in the desired
 mode at the "vga=ask" prompt. For example if you like to use 
 1024x768x256 colors you have to say "305" at this prompt.
 
diff -urN linux-2.2.18-pre15/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- linux-2.2.18-pre15/Documentation/filesystems/affs.txt	Sun Mar 21 07:11:36 1999
+++ linux/Documentation/filesystems/affs.txt	Mon Dec  3 20:33:54 2001
@@ -158,7 +158,7 @@
 have an Amiga harddisk connected to your PC, it will overwrite
 the bytes 0x00dc..0x00df of block 0 with garbage, thus invalidating
 the Rigid Disk Block. Sheer luck has it that this is an unused
-area of the RDB, so only the checksum doesn's match anymore.
+area of the RDB, so only the checksum doesn't match anymore.
 Linux will ignore this garbage and recognize the RDB anyway, but
 before you connect that drive to your Amiga again, you must
 restore or repair your RDB. So please do make a backup copy of it
diff -urN linux-2.2.18-pre15/Documentation/filesystems/coda.txt linux/Documentation/filesystems/coda.txt
--- linux-2.2.18-pre15/Documentation/filesystems/coda.txt	Thu Apr 29 11:53:41 1999
+++ linux/Documentation/filesystems/coda.txt	Mon Dec  3 20:33:54 2001
@@ -1531,7 +1531,7 @@
 
 
   DDeessccrriippttiioonn Remove all entries in the cache lying in a directory
-  CodaFid, and all children of this directory. This call is issed when
+  CodaFid, and all children of this directory. This call is issued when
   Venus receives a callback on the directory.
 
 
@@ -1630,7 +1630,7 @@
 
   The following requirements should be accommodated:
 
-  1. The message queueus should have open and close routines.  On Unix
+  1. The message queues should have open and close routines.  On Unix
      the opening of the character devices are such routines.
 
   +o  Before opening, no messages can be placed.
diff -urN linux-2.2.18-pre15/Documentation/filesystems/fat_cvf.txt linux/Documentation/filesystems/fat_cvf.txt
--- linux-2.2.18-pre15/Documentation/filesystems/fat_cvf.txt	Sun Feb 28 09:47:37 1999
+++ linux/Documentation/filesystems/fat_cvf.txt	Mon Dec  3 20:33:54 2001
@@ -140,7 +140,7 @@
       THE KIND OF CVF I SUPPORT". The function must maintain the module
       usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
       and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
-      successful recongition would lead to a call of the mount_cvf function
+      successful recognition would lead to a call of the mount_cvf function
       later. 
   - mount_cvf:
       A function that sets up some values or initializes something additional
diff -urN linux-2.2.18-pre15/Documentation/filesystems/ufs.txt linux/Documentation/filesystems/ufs.txt
--- linux-2.2.18-pre15/Documentation/filesystems/ufs.txt	Mon Aug  9 12:04:38 1999
+++ linux/Documentation/filesystems/ufs.txt	Mon Dec  3 20:33:54 2001
@@ -9,7 +9,7 @@
 
 ufstype=type_of_ufs
 	UFS is a file system widely used in different operating systems.
-	The problem are differencies among implementations. Features of
+	The problem are differences among implementations. Features of
 	some implementations are undocumented, so its hard to recognize
 	type of ufs automatically. That's why user must specify type of 
 	ufs manually by mount option ufstype. Possible values are:
diff -urN linux-2.2.18-pre15/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX
--- linux-2.2.18-pre15/Documentation/isdn/00-INDEX	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/isdn/00-INDEX	Mon Dec  3 20:33:54 2001
@@ -31,7 +31,7 @@
 README.eicon
 	- info on driver for Eicon active cards.
 README.concap
-	- info on "CONCAP" ecapsulation protocol interface used for X.25.
+	- info on "CONCAP" encapsulation protocol interface used for X.25.
 README.diversion
 	- info on module for isdn diversion services.
 README.sc
diff -urN linux-2.2.18-pre15/Documentation/isdn/INTERFACE.fax linux/Documentation/isdn/INTERFACE.fax
--- linux-2.2.18-pre15/Documentation/isdn/INTERFACE.fax	Wed Aug 25 17:29:45 1999
+++ linux/Documentation/isdn/INTERFACE.fax	Mon Dec  3 20:33:54 2001
@@ -4,7 +4,7 @@
 Description of the fax-subinterface between linklevel and hardwarelevel of 
   isdn4linux. 
 
-  The communication between linklevel (LL) and harwarelevel (HL) for fax
+  The communication between link level (LL) and hardware level (HL) for fax
   is based on the struct T30_s (defined in isdnif.h).
   This struct is allocated in the LL.  
   In order to use fax, the LL provides the pointer to this struct with the 
@@ -60,7 +60,7 @@
   depending on progress and type of connection.
   If the phase changes because of an AT command, the LL driver
   changes this value. Otherwise the HL-driver takes care of it, but
-  only neccessary on call establishment (from IDLE to PHASE_A).
+  only necessary on call establishment (from IDLE to PHASE_A).
   (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E])
 
   -  direction
diff -urN linux-2.2.18-pre15/Documentation/isdn/README linux/Documentation/isdn/README
--- linux-2.2.18-pre15/Documentation/isdn/README	Wed May  3 17:16:30 2000
+++ linux/Documentation/isdn/README	Mon Dec  3 20:33:54 2001
@@ -146,7 +146,7 @@
                             x = 38400: 198
                           Note on value in Reg 19:
                             There is _NO_ common convention for 38400 baud.
-                            The value 198 is choosen arbitrarily. Users
+                            The value 198 is chosen arbitrarily. Users
                             _MUST_ negotiate this value before establishing
                             a connection.
                AT&Sx    Set window-size (x = 1..8) (not yet implemented)
diff -urN linux-2.2.18-pre15/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- linux-2.2.18-pre15/Documentation/isdn/README.HiSax	Wed May  3 17:16:30 2000
+++ linux/Documentation/isdn/README.HiSax	Mon Dec  3 20:33:54 2001
@@ -77,7 +77,7 @@
 
 If you know other passive cards with the Siemens chipset, please let me know.
 To use the PNP cards you need the isapnptools.
-You can combine any card, if there is no conflict between the ressources
+You can combine any card, if there is no conflict between the resources
 (io, mem, irq).
 
 
@@ -532,7 +532,7 @@
       /sbin/isdnctrl huptimeout isdn0 0
       /sbin/isdnctrl l2_prot isdn0 hdlc
       # Attention you must not set an outgoing number !!! This won't work !!!
-      # The incomming number is LEASED0 for the first card, LEASED1 for the
+      # The incoming number is LEASED0 for the first card, LEASED1 for the
       # second and so on.
       /sbin/isdnctrl addphone isdn0 in LEASED0
       # Here is no need to bind the channel.
@@ -551,7 +551,7 @@
 
 Here an example script:
 #!/bin/sh
-# Start/Stop ISDN lesaed line connection
+# Start/Stop ISDN leased line connection
 
 I4L_AS_MODULE=yes
 I4L_REMOTE_IS_CISCO=no
diff -urN linux-2.2.18-pre15/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc
--- linux-2.2.18-pre15/Documentation/isdn/README.sc	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/isdn/README.sc	Mon Dec  3 20:33:54 2001
@@ -93,7 +93,7 @@
     this driver release?
 
 Before you can compile, install and use the SpellCaster ISA ISDN driver, you
-must ensure that the following software is installed, configuraed and running:
+must ensure that the following software is installed, configured and running:
 
 	- Linux kernel 2.0.20 or later with the required init and ps
 	  versions. Please see your distribution vendor for the correct
@@ -189,7 +189,7 @@
 	basic HDLC connection between its two channels.  Two network 
 	interfaces are created and two routes added between the channels.
 
-	i)   using the isdnctrl utitity, add an interface with "addif" and 
+	i)   using the isdnctrl utility, add an interface with "addif" and 
 	     name it "isdn0"
 	ii)  add the outgoing and inbound telephone numbers
 	iii) set the Layer 2 protocol to hdlc
@@ -213,7 +213,7 @@
 	This file is a script used to configure a BRI ISDN TA to establish a 
 	PPP connection 	between the two channels.  The file is almost 
 	identical to the HDLC connection example except that the packet 
-	ecapsulation type has to be set.
+	encapsulation type has to be set.
 	
 	use the same procedure as in the HDLC connection from steps i) to 
 	iii) then, after the Layer 2 protocol is set, set the encapsulation 
@@ -238,7 +238,7 @@
 	This file is a script used to configure a BRI ISDN TA to accept a 
 	Multi Link PPP connection. 
 	
-	i)   using the isdnctrl utitity, add an interface with "addif" and 
+	i)   using the isdnctrl utility, add an interface with "addif" and 
 	     name it "ippp0"
 	ii)  add the inbound telephone number
 	iii) set the Layer 2 protocol to hdlc and the Layer 3 protocol to 
diff -urN linux-2.2.18-pre15/Documentation/joystick-api.txt linux/Documentation/joystick-api.txt
--- linux-2.2.18-pre15/Documentation/joystick-api.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/joystick-api.txt	Mon Dec  3 20:33:54 2001
@@ -29,13 +29,13 @@
 where js_event is defined as
 
 	struct js_event {
-		__u32 time;     /* event timestamp in miliseconds */
+		__u32 time;     /* event timestamp in milliseconds */
 		__s16 value;    /* value */
 		__u8 type;      /* event type */
 		__u8 number;    /* axis/button number */
 	};
 
-If the read is successfull, it will return sizeof(struct js_event), unless
+If the read is successful, it will return sizeof(struct js_event), unless
 you wanted to read more than one event per read as described in section 3.1.
 
 
@@ -225,7 +225,7 @@
 ~~~~~~~~~~~~~~
 
 JSIOCGNAME(len) allows you to get the name string of the joystick - the same
-as is being printed at boot time. The 'len' argument is the lenght of the
+as is being printed at boot time. The 'len' argument is the length of the
 buffer provided by the application asking for the name. It is used to avoid
 possible overrun should the name be too long.
 
diff -urN linux-2.2.18-pre15/Documentation/joystick-parport.txt linux/Documentation/joystick-parport.txt
--- linux-2.2.18-pre15/Documentation/joystick-parport.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/joystick-parport.txt	Mon Dec  3 20:33:54 2001
@@ -22,7 +22,7 @@
 
 2. Devices supported
 ~~~~~~~~~~~~~~~~~~~~
-  Many console and 8-bit coputer gamepads and joysticks are supported. The
+  Many console and 8-bit computer gamepads and joysticks are supported. The
 following subsections discuss usage of each.
 
 2.1 NES and SNES
diff -urN linux-2.2.18-pre15/Documentation/kbuild/commands.txt linux/Documentation/kbuild/commands.txt
--- linux-2.2.18-pre15/Documentation/kbuild/commands.txt	Mon Feb  1 12:03:20 1999
+++ linux/Documentation/kbuild/commands.txt	Mon Dec  3 20:33:54 2001
@@ -84,7 +84,7 @@
 	occasionally.  If you are adding configuration options, it's
 	nice if you do it before you publish your patch!
 
-	You can run 'make checkhelp' withoug configuring the kernel.
+	You can run 'make checkhelp' without configuring the kernel.
 	Also, 'make checkhelp' does not modify any files.
 
     make dep, make depend
diff -urN linux-2.2.18-pre15/Documentation/kbuild/config-language.txt linux/Documentation/kbuild/config-language.txt
--- linux-2.2.18-pre15/Documentation/kbuild/config-language.txt	Wed Jun  7 14:26:42 2000
+++ linux/Documentation/kbuild/config-language.txt	Mon Dec  3 20:33:54 2001
@@ -80,7 +80,7 @@
 
     A /word/ is a single unquoted word, a single-quoted string, or a
     double-quoted string.  If the word is unquoted or double quoted,
-    then $-substition will be performed on the word.
+    then $-substitution will be performed on the word.
 
     A /symbol/ is a single unquoted word.  A symbol must have a name of
     the form CONFIG_*.  scripts/mkdep.c relies on this convention in order
@@ -231,7 +231,7 @@
 trying to write Config Language scripts with a default value for bool,
 but *all* of the existing language interpreters discard additional values.
 Feel free to submit a multi-interpreter patch to linux-kbuild if you
-want to implement this as an enhancment.
+want to implement this as an enhancement.
 
 Configure:  implemented
 Menuconfig: implemented
diff -urN linux-2.2.18-pre15/Documentation/moxa-smartio linux/Documentation/moxa-smartio
--- linux-2.2.18-pre15/Documentation/moxa-smartio	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/moxa-smartio	Mon Dec  3 20:33:54 2001
@@ -23,7 +23,7 @@
     -C168P/H/HS, C168H/PCI 8 port multiport board.
 
    This driver has been modified a little and cleaned up from the Moxa
-   contributed driver code and merged into Linux 2.2.14pre. In paticular
+   contributed driver code and merged into Linux 2.2.14pre. In particular
    official major/minor numbers have been assigned which are different to
    those the original Moxa supplied driver used.
 
@@ -83,7 +83,7 @@
 
        PCI board
        ---------
-       You may need to adjust IRQ useage in BIOS to avoid from IRQ conflict
+       You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict
        with other ISA devices. Please refer to hardware installation
        procedure in User's Manual in advance.
 
@@ -150,7 +150,7 @@
 
 	  # insmod mxser
 
-	  to activate the moduler driver. You may run "lsmod" to check
+	  to activate the modular driver. You may run "lsmod" to check
 	  if "mxser.o" is activated.
 
        2. Create special files by executing "msmknod".
@@ -158,7 +158,7 @@
 	  # ./msmknod
 
 	  Default major numbers for dial-in device and callout device are
-	  174, 175. Msmknod will delete any special files occuping the same
+	  174, 175. Msmknod will delete any special files occupying the same
 	  device naming.
 
        3. Up to now, you may manually execute "insmod mxser" to activate
@@ -209,7 +209,7 @@
 	  below.
 	  a. # cd /moxa/mxser/driver
 	     # vi mxser.c
-	  b. Find the array mxserBoardCAP[] as belows.
+	  b. Find the array mxserBoardCAP[] as below.
 
 	     static int mxserBoardCAP[]
 	     = {0x00, 0x00, 0x00, 0x00};
@@ -260,7 +260,7 @@
 	   f. cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz
 	   g. Please make sure the boot kernel (vmlinuz) is in the
 	      correct position. If you use 'lilo' utility, you should
-	      check /etc/lilo.conf 'image' item specifiedd the path
+	      check /etc/lilo.conf 'image' item specified the path
 	      which is the 'vmlinuz' path, or you will load wrong
 	      (or old) boot kernel image (vmlinuz).
 	   h. chmod 400 /vmlinuz
@@ -372,7 +372,7 @@
 -----------------------------------------------------------------------------
 6. Troubleshooting
 
-   The boot time error mesages and solutions are stated as clearly as
+   The boot time error messages and solutions are stated as clearly as
    possible. If all the possible solutions fail, please contact our technical
    support team to get more help.
 
@@ -388,7 +388,7 @@
    which device causes the situation,please check /proc/interrupts to find
    free IRQ and simply change another free IRQ for Moxa board.
 
-   Error msg: Board #: C1xx Series(CAP=xxx) interupt number invalid.
+   Error msg: Board #: C1xx Series(CAP=xxx) interrupt number invalid.
    Solution:
    Each port within the same multiport board shares the same IRQ. Please set
    one IRQ (IRQ doesn't equal to zero) for one Moxa board.
diff -urN linux-2.2.18-pre15/Documentation/networking/README.sb1000 linux/Documentation/networking/README.sb1000
--- linux-2.2.18-pre15/Documentation/networking/README.sb1000	Tue Oct 26 17:53:39 1999
+++ linux/Documentation/networking/README.sb1000	Mon Dec  3 20:33:54 2001
@@ -30,7 +30,7 @@
       http://home.adelphia.net/~siglercm/sb1000.html
       http://linuxpower.cx/~cable/
 
-   along with these utilties.
+   along with these utilities.
 
 3.) The standard isapnp tools.  These are necessary to configure your SB1000
 card at boot time (or afterwards by hand) since it's a PnP card.
diff -urN linux-2.2.18-pre15/Documentation/networking/README.xpeed linux/Documentation/networking/README.xpeed
--- linux-2.2.18-pre15/Documentation/networking/README.xpeed	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/README.xpeed	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,52 @@
+Driver for Xpeed Inc. PCI network cards
+---------------------------------------
+The following cards are supported with this driver:
+
+Xpeed X200 IDSL NIC (http://www.xpeed.com/Products/x200/x200_c.html)
+Xpeed X300 SDSL NIC (http://www.xpeed.com/Products/x300/x300_c.html)
+
+This driver handles frame relay encapsulation of WAN link and presents
+the card to the kernel as an ethernet-like device called dsl0, dsl1, etc.
+
+The driver supports up to 6 cards and supports operation in either
+RFC1490 frame relay or bridged ethernet modes. Note that when operating
+in RFC1490 frame relay mode, "ifconfig -arp" (no ARP) should be used.
+
+=============================== NOTE ====================================
+You will NEED to use the xpds-config utility to configure the link speed,
+change LT/NT mode, change swap/invert parameters, and reflash the adapter
+firmware. This utility and other support files can be downloaded at
+ftp://ftp.xpeed.com/pub/linux/
+=============================== NOTE ====================================
+
+The driver accepts the following parameters when compiled as a module:
+
+xpds_default_dlci
+    Defines the DLCI to be used for the local interface. Default is 16.
+
+xpds_default_dlci_cr
+    A special mask value to be used when speaking with certain buggy
+    vendor equipment. Can be 0 or 2. Default is 0.
+
+xpds_default_dlci_lmi
+    Defines how the card will perform LMI protocol negotiation.
+    Valid values are:
+     0  No LMI will be used.
+     1  LMI will operate in LT (Line Termination) mode.
+     2  LMI will operate in NT (Network Termination) mode.
+     3  LMI will operation in bidirectional NT mode.
+    -1  Attempt to autoconfigure LMI mode. (Default)
+
+xpds_default_bridged
+    Defines whether the card will operate in RFC1490 frame relay or
+    bridged ethernet mode. Default is 0, RFC1490 frame relay mode.
+
+Typical usage for a connection in RFC1490 frame relay mode:
+
+# modprobe xpds-fr
+# ifconfig dsl0 10.0.0.2 netmask 255.255.255.0 broadcast 10.0.0.255 -arp
+
+Typical usage for a connection in bridged ethernet mode:
+
+# modprobe xpds-fr xpds_default_bridged=1
+# ifconfig dsl0 10.0.0.2 netmask 255.255.255.0 broadcast 10.0.0.255
diff -urN linux-2.2.18-pre15/Documentation/networking/comx.txt linux/Documentation/networking/comx.txt
--- linux-2.2.18-pre15/Documentation/networking/comx.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/networking/comx.txt	Mon Dec  3 20:33:54 2001
@@ -63,7 +63,7 @@
 
 When you're ready with filling in the files in the comx[n] directory, you can
 configure the corresponding network interface with the standard network 
-configuration utilites. If you're unble to bring the interfaces up, look up
+configuration utilities. If you're unable to bring the interfaces up, look up
 the various kernel log files on your system, and consult the messages for
 a probable reason.
 
diff -urN linux-2.2.18-pre15/Documentation/networking/dmfe.txt linux/Documentation/networking/dmfe.txt
--- linux-2.2.18-pre15/Documentation/networking/dmfe.txt	Wed May  3 17:16:30 2000
+++ linux/Documentation/networking/dmfe.txt	Mon Dec  3 20:33:54 2001
@@ -29,7 +29,7 @@
            /net/inet -Wall -Wstrict-prototypes -O6 -c dmfe.c"
 
 
-  B. The following steps teach you how to active DM9102 board:
+  B. The following steps teach you how to activate a DM9102 board:
 
         1. Used the upper compiler command to compile dmfe.c
 
@@ -44,13 +44,13 @@
            "ifconfig eth0 172.22.3.18"
                           ^^^^^^^^^^^ Your IP address
 
-        4. Active the IP routing table. For some distributions, it is not
+        4. Activate the IP routing table. For some distributions, it is not
            necessary. You can type "route" to check.
 
            "route add default eth0"
 
 
-        5. Well done. Your DM9102 adapter actived now.
+        5. Well done. Your DM9102 adapter is now activated.
 
 
    C. Object files description:
diff -urN linux-2.2.18-pre15/Documentation/networking/olympic.txt linux/Documentation/networking/olympic.txt
--- linux-2.2.18-pre15/Documentation/networking/olympic.txt	Mon Aug  9 12:04:38 1999
+++ linux/Documentation/networking/olympic.txt	Mon Dec  3 20:33:54 2001
@@ -38,14 +38,14 @@
 message_level: Controls level of messages created by the driver. Defaults to 0:
 which only displays start-up and critical messages.  Presently any non-zero 
 value will display all soft messages as well.  NB This does not turn 
-debuging messages on, that must be done by modified the source code.
+debugging messages on, that must be done by modified the source code.
 
 Multi-card:
 
 The driver will detect multiple cards and will work with shared interrupts,
 each card is assigned the next token ring device, i.e. tr0 , tr1, tr2.  The 
 driver should also happily reside in the system with other drivers.  It has 
-been tested with ibmtr.c running, and I personnally have had one Olicom PCI 
+been tested with ibmtr.c running, and I personally have had one Olicom PCI 
 card and two IBM olympic cards (all on the same interrupt), all running
 together. 
 
@@ -68,7 +68,7 @@
 by the driver and the source and destination addresses printed. 
 Also an entry will be added in  /proc/net called olympic_tr. This 
 displays low level information about the configuration of the ring and
-the adapter. This feature has been designed for network adiministrators
+the adapter. This feature has been designed for network administrators
 to assist in the diagnosis of network / ring problems.
 
 6/8/99 Peter De Schrijver and Mike Phillips
diff -urN linux-2.2.18-pre15/Documentation/networking/shaper.txt linux/Documentation/networking/shaper.txt
--- linux-2.2.18-pre15/Documentation/networking/shaper.txt	Wed Jun  7 14:26:42 2000
+++ linux/Documentation/networking/shaper.txt	Mon Dec  3 20:33:54 2001
@@ -42,7 +42,7 @@
 
 	There is no "borrowing" or "sharing" scheme. This is a simple
 traffic limiter. We implement Van Jacobson and Sally Floyd's CBQ
-architecture into Linux 2.2. THis is the preferred solution. Shaper is
+architecture into Linux 2.2. This is the preferred solution. Shaper is
 for simple or back compatible setups.
 
 Alan
diff -urN linux-2.2.18-pre15/Documentation/networking/sis900.txt linux/Documentation/networking/sis900.txt
--- linux-2.2.18-pre15/Documentation/networking/sis900.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/networking/sis900.txt	Mon Dec  3 20:33:54 2001
@@ -161,7 +161,7 @@
 
   Silicon Integrated System Corp. is cooperating closely with core Linux
   Kernel developers. The revisions of SiS 900 driver are distributed by
-  the usuall channels for kernel tar files and patches. Those kernel tar
+  the usual channels for kernel tar files and patches. Those kernel tar
   files for official kernel and patches for kernel pre-release can be
   download at official kernel ftp site
   <http://ftp.kernel.org/pub/linux/kernel/> and its mirrors.  The 1.06
diff -urN linux-2.2.18-pre15/Documentation/networking/sk98lin.txt linux/Documentation/networking/sk98lin.txt
--- linux-2.2.18-pre15/Documentation/networking/sk98lin.txt	Wed May  3 17:16:30 2000
+++ linux/Documentation/networking/sk98lin.txt	Mon Dec  3 20:33:54 2001
@@ -138,7 +138,7 @@
 options sk98lin ...
 
 For "...", use the same syntax as described below for the command
-line paramaters of insmod.
+line parameters of insmod.
 You either have to reboot your computer or unload and reload
 the driver to activate the new parameters.
 The syntax of the driver parameters is:
@@ -187,7 +187,7 @@
   this port is not "Sense". If autonegotiation is "On", all
   three values are possible. If it is "Off", only "Full" and
   "Half" are allowed.
-  It is usefull if your link partner does not support all
+  It is useful if your link partner does not support all
   possible combinations.
 
 - Flow Control
@@ -269,7 +269,7 @@
 
 Large frames (also called jumbo frames) are now supported by the
 driver. This can result in a greatly improved throughput if
-transfering large amounts of data.
+transferring large amounts of data.
 To enable large frames, set the MTU (maximum transfer unit)
 of the interface to the value you wish (up to 9000). The command
 for this is:
@@ -285,7 +285,7 @@
 You can switch back to the standard ethernet frame size with:
   ifconfig eth0 mtu 1500
 
-To make this setting persitent, add a script with the 'ifconfig'
+To make this setting persistent, add a script with the 'ifconfig'
 line to the system startup sequence (named something like "S99sk98lin"
 in /etc/rc.d/rc2.d).
 ***
diff -urN linux-2.2.18-pre15/Documentation/networking/tlan.txt linux/Documentation/networking/tlan.txt
--- linux-2.2.18-pre15/Documentation/networking/tlan.txt	Mon Sep  4 10:39:16 2000
+++ linux/Documentation/networking/tlan.txt	Mon Dec  3 20:33:54 2001
@@ -44,7 +44,7 @@
 	   0x01		Turn on general debugging messages.
 	   0x02		Turn on receive debugging messages.
 	   0x04		Turn on transmit debugging messages.
-	   0x08		Turn on list debugging messsages.
+	   0x08		Turn on list debugging messages.
 
 	2. You can append aui=1 to the end of the insmod line to cause
            the adapter to use the AUI interface instead of the 10 Base T
diff -urN linux-2.2.18-pre15/Documentation/networking/wan-router.txt linux/Documentation/networking/wan-router.txt
--- linux-2.2.18-pre15/Documentation/networking/wan-router.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/networking/wan-router.txt	Mon Dec  3 20:33:54 2001
@@ -142,11 +142,11 @@
 
 2.0.8   Nov 02, 1999            - Fixed up the X25API code.
                                 - Clear call bug fixed.i
-                                - Eanbled driver for multi-card
+                                - Enabled driver for multi-card
                                   operation.                                
 
 2.0.7   Aug 26, 1999		- Merged X25API code into WANPIPE.
-				- Fixed a memeory leak for X25API
+				- Fixed a memory leak for X25API
 				- Updated the X25API code for 2.2.X kernels.
 				- Improved NEM handling.
 
diff -urN linux-2.2.18-pre15/Documentation/networking/wanpipe.txt linux/Documentation/networking/wanpipe.txt
--- linux-2.2.18-pre15/Documentation/networking/wanpipe.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/networking/wanpipe.txt	Mon Dec  3 20:33:54 2001
@@ -228,7 +228,7 @@
 				creating applications using BiSync
    				streaming.        
 
-2.0.5   Aug 04, 1999 		CHDLC initializatin bug fix.
+2.0.5   Aug 04, 1999 		CHDLC initialization bug fix.
 				PPP interrupt driven driver: 
   				Fix to the PPP line hangup problem.
 				New PPP firmware
@@ -241,13 +241,13 @@
 				Streaming HDLC API has been taken out.  
 				 Available as a patch.                 
 
-2.0.6   Aug 17, 1999		Increased debugging in statup scripts
-				Fixed insallation bugs from 2.0.5
+2.0.6   Aug 17, 1999		Increased debugging in startup scripts
+				Fixed installation bugs from 2.0.5
 				Kernel patch works for both 2.2.10 and 2.2.11 kernels.
 				There is no functional difference between the two packages         
 
 2.0.7   Aug 26, 1999		o  Merged X25API code into WANPIPE.
-				o  Fixed a memeory leak for X25API
+				o  Fixed a memory leak for X25API
 				o  Updated the X25API code for 2.2.X kernels.
 				o  Improved NEM handling.   
 
diff -urN linux-2.2.18-pre15/Documentation/s390/DASD linux/Documentation/s390/DASD
--- linux-2.2.18-pre15/Documentation/s390/DASD	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/s390/DASD	Mon Dec  3 20:33:54 2001
@@ -30,7 +30,7 @@
 We currently implement one partition per volume, which is the whole
 volume, skipping the first blocks up to the volume label. These are
 reserved for IPL records and IBM's volume label to assure
-accessability of the DASD from other OSs. In a later stage we will
+accessibility of the DASD from other OSs. In a later stage we will
 provide support of partitions, maybe VTOC oriented or using a kind of
 partition table in the label record.
 
@@ -38,7 +38,7 @@
 
 -Low-level format (?CKD only)
 For using an ECKD-DASD as a Linux harddisk you have to low-level
-format the tracks by issueing the BLKDASDFORMAT-ioctl on that
+format the tracks by issuing the BLKDASDFORMAT-ioctl on that
 device. This will erase any data on that volume including IBM volume
 labels, VTOCs etc. The ioctl may take a 'struct format_data *' or
 'NULL' as an argument.  
diff -urN linux-2.2.18-pre15/Documentation/s390/cds.txt linux/Documentation/s390/cds.txt
--- linux-2.2.18-pre15/Documentation/s390/cds.txt	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/s390/cds.txt	Mon Dec  3 20:33:54 2001
@@ -778,7 +778,7 @@
 The halt_IO() function returns :
 
       0 - successful completion or request successfuly initiated
--EBUSY  - the device is currently performing a sysnchonous I/O
+-EBUSY  - the device is currently performing a synchronous I/O
           operation : do_IO() with flag DOIO_WAIT_FOR_INTERRUPT
           or an error was encountered and the device is currently
           be sensed
diff -urN linux-2.2.18-pre15/Documentation/sound/ESS linux/Documentation/sound/ESS
--- linux-2.2.18-pre15/Documentation/sound/ESS	Mon Feb  1 14:04:39 1999
+++ linux/Documentation/sound/ESS	Mon Dec  3 20:33:54 2001
@@ -12,7 +12,7 @@
 master volume control.
 
 Every chip that's detected as a ES1887 now has Full Duplex support. Made a 
-little testprogram that showes that is works, haven't seen a real program that
+little test program that shows that is works, haven't seen a real program that
 needs this however.
 
 For ESS chips an additional parameter "esstype" can be specified. This controls
diff -urN linux-2.2.18-pre15/Documentation/sound/Introduction linux/Documentation/sound/Introduction
--- linux-2.2.18-pre15/Documentation/sound/Introduction	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/sound/Introduction	Mon Dec  3 20:33:54 2001
@@ -322,7 +322,7 @@
 
 7)  Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB).
 
-8)  If the system reports insuffcient DMA memory then you may want to
+8)  If the system reports insufficient DMA memory then you may want to
     load sound with the "dmabufs=1" option. Or in /etc/conf.modules add
 	
 	preinstall sound dmabufs=1
@@ -356,7 +356,7 @@
 
 When a sound card is first referenced and sound is modular the sound system
 will ask for the sound devices to be loaded. Initially it requests that
-the driver for the sound system is loaded. It then wwill ask for 
+the driver for the sound system is loaded. It then will ask for 
 sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and
 so on). Thus you can do
 
diff -urN linux-2.2.18-pre15/Documentation/sound/Maestro linux/Documentation/sound/Maestro
--- linux-2.2.18-pre15/Documentation/sound/Maestro	Wed May  3 17:16:30 2000
+++ linux/Documentation/sound/Maestro	Mon Dec  3 20:33:54 2001
@@ -70,7 +70,7 @@
 
 As this is a PCI device, the module does not need to be informed of
 any IO or IRQ resources it should use, it devines these from the
-system.  Somtimes, on sucky PCs, the BIOS fails to allocated resources
+system.  Sometimes, on sucky PCs, the BIOS fails to allocated resources
 for the maestro.  This will result in a message like:
 	maestro: PCI subsystem reports IRQ 0, this might not be correct.
 from the kernel.  Should this happen the sound chip most likely will
diff -urN linux-2.2.18-pre15/Documentation/sound/PSS linux/Documentation/sound/PSS
--- linux-2.2.18-pre15/Documentation/sound/PSS	Tue Jan  4 10:12:10 2000
+++ linux/Documentation/sound/PSS	Mon Dec  3 20:33:54 2001
@@ -3,7 +3,7 @@
 device. The PSS driver enables MSS and MPU401 modes of the card. SB 
 is not enabled since it doesn't work concurrently with MSS. 
 
-If you build this driver as a module then the driver takes the folowing
+If you build this driver as a module then the driver takes the following
 parameters
 
 pss_io. 	The I/O base the PSS card is configured at (normally 0x220
diff -urN linux-2.2.18-pre15/Documentation/sound/README.OSS linux/Documentation/sound/README.OSS
--- linux-2.2.18-pre15/Documentation/sound/README.OSS	Thu Apr 29 11:53:41 1999
+++ linux/Documentation/sound/README.OSS	Mon Dec  3 20:33:54 2001
@@ -81,7 +81,7 @@
 	Gregor Hoffleit	Mozart support (initial version)
 	Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support
 	James Hightower Spotting a tiny but important bug in CS423x support.
-	Denis Sablic	OPTi 82C924 spesific enhancements (non PnP mode)
+	Denis Sablic	OPTi 82C924 specific enhancements (non PnP mode)
 	Tim MacKenzie	Full duplex support for OPTi 82C930.
 	
 	Please look at lowlevel/README for more contributors.
@@ -542,7 +542,7 @@
 
 	There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They
 	are PnP chips and will not work with the OPL3-SA1 driver. You should 
-	use the standard MSS, MPU401 and OPL3 options with thses chips and to
+	use the standard MSS, MPU401 and OPL3 options with these chips and to
 	activate the card using isapnptools.
 
 4Front Technologies SoftOSS
@@ -1309,7 +1309,7 @@
 NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
 of OSS doesn't work with it.
 
-There are some ES1688/688 based sound cards and (particularily) motherboards
+There are some ES1688/688 based sound cards and (particularly) motherboards
 which use software configurable I/O port relocation feature of the chip.
 This ESS proprietary feature is supported only by OSS/Linux.
 
@@ -1319,7 +1319,7 @@
 work.
 
 ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688
-brobably works with OSS/Free after initialization using isapnptools.
+probably works with OSS/Free after initialization using isapnptools.
 
 Reveal cards
 ------------
diff -urN linux-2.2.18-pre15/Documentation/sound/README.modules linux/Documentation/sound/README.modules
--- linux-2.2.18-pre15/Documentation/sound/README.modules	Sun Jan 17 18:23:01 1999
+++ linux/Documentation/sound/README.modules	Mon Dec  3 20:33:54 2001
@@ -71,7 +71,7 @@
 The sound modules normally allocate DMA buffers during open() and
 deallocate them during close(). Linux can often have problems allocating
 DMA buffers for ISA cards on machines with more than 16MB RAM. This is
-because ISA DMA buffers must exist below the 16MB boundry and it is quite
+because ISA DMA buffers must exist below the 16MB boundary and it is quite
 possible that we can't find a large enough free block in this region after
 the machine has been running for any amount of time. The way to avoid this
 problem is to allocate the DMA buffers during module load and deallocate
diff -urN linux-2.2.18-pre15/Documentation/stallion.txt linux/Documentation/stallion.txt
--- linux-2.2.18-pre15/Documentation/stallion.txt	Sun Mar 28 09:02:27 1999
+++ linux/Documentation/stallion.txt	Mon Dec  3 20:33:54 2001
@@ -62,7 +62,7 @@
 boards.
 
 There are two methods of configuring ISA, EISA and MCA boards into the drivers.
-If using the driver as a loadable module then the simplist method is to pass
+If using the driver as a loadable module then the simplest method is to pass
 the driver configuration as module arguments. The other method is to modify
 the driver source to add configuration lines for each board in use.
 
@@ -108,7 +108,7 @@
 
 where:
 
-    board?  -- specifies the arbitary board number of this board,
+    board?  -- specifies the arbitrary board number of this board,
                can be in the range 0 to 3.
 
     name    -- textual name of this board. The board name is the comman
diff -urN linux-2.2.18-pre15/Documentation/sysctl/vm.txt linux/Documentation/sysctl/vm.txt
--- linux-2.2.18-pre15/Documentation/sysctl/vm.txt	Mon Sep  4 10:39:16 2000
+++ linux/Documentation/sysctl/vm.txt	Mon Dec  3 20:33:54 2001
@@ -118,11 +118,11 @@
 		reaches this number, only the kernel can
 		allocate more memory.
 freepages.low	If the number of free pages gets below this
-		point, the kernel starts swapping agressively.
+		point, the kernel starts swapping aggressively.
 freepages.high	The kernel tries to keep up to this amount of
 		memory free; if memory comes below this point,
 		the kernel gently starts swapping in the hopes
-		that it never has to do real agressive swapping.
+		that it never has to do real aggressive swapping.
 
 ==============================================================
 
@@ -198,7 +198,7 @@
 - swap cache
 
 When your system is both deep in swap and high on cache,
-it probably means that a lot of the swaped data is being
+it probably means that a lot of the swapped data is being
 cached, making for more efficient swapping than possible
 with the 2.0 kernel.
 
@@ -213,7 +213,7 @@
 On a low-memory, single CPU system you can safely set these
 values to 0 so you don't waste the memory. On SMP systems it
 is used so that the system can do fast pagetable allocations
-without having to aquire the kernel memory lock.
+without having to acquire the kernel memory lock.
 
 For large systems, the settings are probably OK. For normal
 systems they won't hurt a bit. For small systems (<16MB ram)
diff -urN linux-2.2.18-pre15/Documentation/usb/bluetooth.txt linux/Documentation/usb/bluetooth.txt
--- linux-2.2.18-pre15/Documentation/usb/bluetooth.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/usb/bluetooth.txt	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,44 @@
+INTRODUCTION
+
+  The USB Bluetooth driver supports any USB Bluetooth device.
+  It currently works well with the Linux USB Bluetooth stack from Axis 
+  (available at http://developer.axis.com/software/bluetooth/ ) and 
+  has been rumored to work with other Linux USB Bluetooth stacks.
+
+
+CONFIGURATION
+
+  Currently the driver can handle up to 256 different USB Bluetooth 
+  devices at once. 
+
+  If you are not using devfs:
+    The major number that the driver uses is 216 so to use the driver,
+    create the following nodes:
+	mknod /dev/ttyUB0 c 216 0
+	mknod /dev/ttyUB1 c 216 1
+	mknod /dev/ttyUB2 c 216 2
+	mknod /dev/ttyUB3 c 216 3
+		.
+		.
+		.
+	mknod /dev/ttyUB254 c 216 254
+	mknod /dev/ttyUB255 c 216 255
+
+  If you are using devfs:
+    The devices supported by this driver will show up as
+    /dev/usb/ttub/{0,1,...}
+
+  When the device is connected and recognized by the driver, the driver
+  will print to the system log, which node the device has been bound to.
+
+
+CONTACT:
+
+  If anyone has any problems using this driver, please contact me, or 
+  join the Linux-USB mailing list (information on joining the mailing 
+  list, as well as a link to its searchable archive is at 
+  http://www.linux-usb.org/ )
+
+
+Greg Kroah-Hartman
+greg@kroah.com
diff -urN linux-2.2.18-pre15/Documentation/video4linux/CQcam.txt linux/Documentation/video4linux/CQcam.txt
--- linux-2.2.18-pre15/Documentation/video4linux/CQcam.txt	Wed May  3 17:16:30 2000
+++ linux/Documentation/video4linux/CQcam.txt	Mon Dec  3 20:33:54 2001
@@ -127,7 +127,7 @@
 
   The c-qcam is IEEE1284 compatible, so if you are using the proc file
 system (CONFIG_PROC_FS), the parallel printer support
-(CONFIG_PRINTER), the IEEE 1284 sytem,(CONFIG_PRINTER_READBACK), you
+(CONFIG_PRINTER), the IEEE 1284 system,(CONFIG_PRINTER_READBACK), you
 should be able to read some identification from your quickcam with
 
          modprobe -v parport
diff -urN linux-2.2.18-pre15/Documentation/video4linux/README.cpia linux/Documentation/video4linux/README.cpia
--- linux-2.2.18-pre15/Documentation/video4linux/README.cpia	Wed Jun  7 14:26:42 2000
+++ linux/Documentation/video4linux/README.cpia	Mon Dec  3 20:33:54 2001
@@ -173,7 +173,7 @@
 
 The camera can act in two modes, streaming or grabbing. Right now a
 polling grab-scheme is used. Maybe interrupt driven streaming will be
-used for a ansychronous mmap interface in the next major release of the
+used for a asynchronous mmap interface in the next major release of the
 driver. This might give a better frame rate.
 
 ---------------------------------------------------------------------------
diff -urN linux-2.2.18-pre15/Documentation/video4linux/radiotrack.txt linux/Documentation/video4linux/radiotrack.txt
--- linux-2.2.18-pre15/Documentation/video4linux/radiotrack.txt	Sun Aug 23 13:32:25 1998
+++ linux/Documentation/video4linux/radiotrack.txt	Mon Dec  3 20:33:54 2001
@@ -35,7 +35,7 @@
 --------------------
 The RadioTrack card is an ISA 8-bit FM radio card.  The radio frequency (RF)
 input is simply an antenna lead, and the output is a power audio signal
-available through a miniature phono plug.  Its RF frequencies of operation are
+available through a miniature phone plug.  Its RF frequencies of operation are
 more or less limited from 87.0 to 109.0 MHz (the commercial FM broadcast
 band).  Although the registers can be programmed to request frequencies beyond
 these limits, experiments did not give promising results.  The variable
diff -urN linux-2.2.18-pre15/Makefile linux/Makefile
--- linux-2.2.18-pre15/Makefile	Mon Dec  3 20:33:28 2001
+++ linux/Makefile	Mon Dec  3 20:33:54 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 18
-EXTRAVERSION = pre15
+EXTRAVERSION = pre16
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -urN linux-2.2.18-pre15/arch/alpha/kernel/core_mcpcia.c linux/arch/alpha/kernel/core_mcpcia.c
--- linux-2.2.18-pre15/arch/alpha/kernel/core_mcpcia.c	Wed Jun  7 14:26:42 2000
+++ linux/arch/alpha/kernel/core_mcpcia.c	Mon Dec  3 20:33:54 2001
@@ -190,7 +190,7 @@
 {
 	unsigned long addr;
 
-	if (!pci_probe_enabled)
+	if (!pci_probe_enabled || !hose->pci_config_space)
 		return -1;
 
 	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
diff -urN linux-2.2.18-pre15/arch/alpha/kernel/core_tsunami.c linux/arch/alpha/kernel/core_tsunami.c
--- linux-2.2.18-pre15/arch/alpha/kernel/core_tsunami.c	Wed May  3 17:16:30 2000
+++ linux/arch/alpha/kernel/core_tsunami.c	Mon Dec  3 20:33:54 2001
@@ -91,7 +91,7 @@
 {
 	unsigned long addr;
 
-	if (!pci_probe_enabled)
+	if (!pci_probe_enabled || !hose->pci_config_space)
 		return -1;
 
 	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, "
diff -urN linux-2.2.18-pre15/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- linux-2.2.18-pre15/arch/alpha/kernel/smp.c	Mon Dec  3 20:33:28 2001
+++ linux/arch/alpha/kernel/smp.c	Mon Dec  3 20:33:54 2001
@@ -543,12 +543,12 @@
 	bogosum = 0;
         for (i = 0; i < NR_CPUS; i++) {
 		if (cpu_present_mask & (1L << i))
-			bogosum += cpu_data[i].loops_per_sec;
+			bogosum += cpu_data[i].loops_per_jiffy;
         }
 	printk(KERN_INFO "SMP: Total of %d processors activated "
 	       "(%lu.%02lu BogoMIPS).\n",
-	       cpu_count, (bogosum + 2500) / 500000,
-	       ((bogosum + 2500) / 5000) % 100);
+	       cpu_count, (bogosum + 2500) / (500000 / HZ),
+	       ((bogosum + 2500) / (5000 / HZ)) % 100);
 
 	smp_num_cpus = cpu_count;
 }
diff -urN linux-2.2.18-pre15/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux-2.2.18-pre15/arch/i386/kernel/setup.c	Mon Dec  3 20:33:28 2001
+++ linux/arch/i386/kernel/setup.c	Mon Dec  3 20:33:54 2001
@@ -1142,10 +1142,10 @@
 	char *p = buffer;
 	int sep_bug;
 	static char *x86_cap_flags[] = {
-	        "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce",
-	        "cx8", "9", "10", "sep", "mtrr", "pge", "14", "cmov",
-	        "16", "17", "psn", "19", "20", "21", "22", "mmx",
-	        "24", "kni", "26", "27", "28", "29", "30", "31"
+	        "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
+	        "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
+	        "16", "pse36", "psn", "19", "20", "21", "22", "mmx",
+	        "24", "xmm", "26", "27", "28", "29", "30", "31"
 	};
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
@@ -1192,7 +1192,8 @@
 				x86_cap_flags[10] = "sep";
 			if (c->x86 < 6)
 				x86_cap_flags[16] = "fcmov";
-			x86_cap_flags[16] = "pat";
+			else
+				x86_cap_flags[16] = "pat";
 			x86_cap_flags[22] = "mmxext";
 			x86_cap_flags[24] = "fxsr";
 			x86_cap_flags[30] = "3dnowext";
@@ -1200,17 +1201,11 @@
 			break;
 																																										
 		    case X86_VENDOR_INTEL:
-			x86_cap_flags[6] = "pae";
-			x86_cap_flags[9] = "apic";
-			x86_cap_flags[14] = "mca";
 			x86_cap_flags[16] = "pat";
-			x86_cap_flags[17] = "pse36";
-			x86_cap_flags[18] = "psn";
 			x86_cap_flags[19] = "cflush";
 			x86_cap_flags[21] = "dtrace";
 			x86_cap_flags[22] = "acpi";
 			x86_cap_flags[24] = "fxsr";
-			x86_cap_flags[25] = "xmm";
 			x86_cap_flags[26] = "xmm2";
 			x86_cap_flags[27] = "ssnp";
 			x86_cap_flags[29] = "acc";
diff -urN linux-2.2.18-pre15/arch/m68k/Makefile linux/arch/m68k/Makefile
--- linux-2.2.18-pre15/arch/m68k/Makefile	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/Makefile	Mon Dec  3 20:33:54 2001
@@ -116,6 +116,11 @@
 SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060
 endif
 
+ifdef CONFIG_FPU_EMU
+CORE_FILES := $(CORE_FILES) arch/m68k/math-emu/mathemu.o
+SUBDIRS := $(SUBDIRS) arch/m68k/math-emu
+endif
+
 lilo:	vmlinux
 	if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi
 	if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
@@ -127,13 +132,26 @@
 
 vmlinux.gz: vmlinux
 
-ifdef CONFIG_KGDB
+ifndef CONFIG_KGDB
 	cp vmlinux vmlinux.tmp
 	$(STRIP) vmlinux.tmp
 	gzip -9c vmlinux.tmp >vmlinux.gz
 	rm vmlinux.tmp
 else
 	gzip -9c vmlinux >vmlinux.gz
+endif
+
+bzImage: vmlinux.bz2
+
+vmlinux.bz2: vmlinux
+
+ifndef CONFIG_KGDB
+	cp vmlinux vmlinux.tmp
+	$(STRIP) vmlinux.tmp
+	bzip2 -1c vmlinux.tmp >vmlinux.bz2
+	rm vmlinux.tmp
+else
+	bzip2 -1c vmlinux >vmlinux.bz2
 endif
 
 archclean:
diff -urN linux-2.2.18-pre15/arch/m68k/bvme6000/config.c linux/arch/m68k/bvme6000/config.c
--- linux-2.2.18-pre15/arch/m68k/bvme6000/config.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/bvme6000/config.c	Mon Dec  3 20:33:54 2001
@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 
+#include <asm/bootinfo.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/setup.h>
@@ -64,6 +65,15 @@
 
 static void (*tick_handler)(int, void *, struct pt_regs *);
 
+
+int bvme6000_parse_bootinfo(const struct bi_record *bi)
+{
+	if (bi->tag == BI_VME_TYPE)
+		return 0;
+	else
+		return 1;
+}
+
 int bvme6000_kbdrate (struct kbd_repeat *k)
 {
 	return 0;
@@ -164,7 +174,7 @@
         unsigned long *old = (unsigned long *)0xf8000000;
 
         /* Wait for button release */
-	while (*config_reg_ptr & BVME_ABORT_STATUS)
+	while (*(volatile unsigned char *)BVME_LOCAL_IRQ_STAT & BVME_ABORT_STATUS)
 		;
 
         *(new+4) = *(old+4);            /* Illegal instruction */
@@ -405,15 +415,6 @@
 
 /*-------------------  Serial console stuff ------------------------*/
 
-static void bvme_scc_write(struct console *co, const char *str, unsigned cnt);
-
-
-void bvme6000_init_console_port (struct console *co, int cflag)
-{
-        co->write = bvme_scc_write;
-}
-
-
 static void scc_delay (void)
 {
         int n;
@@ -423,6 +424,7 @@
 		trash = n;
 }
 
+
 static void scc_write (char ch)
 {
         volatile char *p = (volatile char *)BVME_SCC_A_ADDR;
@@ -431,10 +433,7 @@
                 scc_delay();
         }
         while (!(*p & 4));
-        scc_delay();
-        *p = 8;
-        scc_delay();
-        *p = ch;
+        *(p + 4) = ch;
 }
 
 
@@ -452,5 +451,24 @@
                 scc_write (*str++);
         }
         restore_flags(flags);
+}
+
+
+static int bvme_scc_wait_key (struct console *co)
+{
+	volatile unsigned char *p = (volatile char *)BVME_SCC_A_ADDR;
+
+	/* wait for rx buf filled */
+	while ((*p & 0x01) == 0)
+		;
+
+	return  *(p + 4);
+}
+
+
+void bvme6000_init_console_port (struct console *co, int cflag)
+{
+        co->write    = bvme_scc_write;
+        co->wait_key = bvme_scc_wait_key;
 }
 
diff -urN linux-2.2.18-pre15/arch/m68k/config.in linux/arch/m68k/config.in
--- linux-2.2.18-pre15/arch/m68k/config.in	Wed Jun  7 14:26:42 2000
+++ linux/arch/m68k/config.in	Mon Dec  3 20:33:54 2001
@@ -58,6 +58,13 @@
 bool '68030 support' CONFIG_M68030
 bool '68040 support' CONFIG_M68040
 bool '68060 support' CONFIG_M68060
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'Math emulation support' CONFIG_FPU_EMU
+  if [ "$CONFIG_FPU_EMU" = "y" ]; then
+    bool 'Math emulation extra precision' CONFIG_FPU_EMU_EXTRAPREC
+    bool 'Math emulation only kernel' CONFIG_FPU_EMU_ONLY
+  fi
+fi
 bool 'Advanced configuration options' CONFIG_ADVANCED
 if [ "$CONFIG_ADVANCED" = "y" ]; then
   bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS
@@ -115,7 +122,7 @@
        fi
     fi
   fi
-  if [ "$CONFIG_ATARI" == "y" ]; then
+  if [ "$CONFIG_ATARI" = "y" ]; then
     dep_tristate '    Atari builtin port' CONFIG_PARPORT_ATARI $CONFIG_PARPORT
   fi
 fi
@@ -190,8 +197,8 @@
   fi
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-  bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI
-  dep_tristate 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI
+  tristate 'Macintosh NCR5380 SCSI' CONFIG_MAC_SCSI
+  dep_tristate 'Macintosh NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI
 fi
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 
@@ -251,9 +258,15 @@
   tristate 'Apollo 3c505 support' CONFIG_APOLLO_ELPLUS
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-  bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT
-#  bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE
-  bool 'Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC
+  tristate 'Macintosh NS 8390 based ethernet cards (new driver)' CONFIG_MAC8390
+  if [ "$CONFIG_MAC8390" = "n" ]; then
+    bool 'Macintosh NS 8390 based ethernet cards (old driver)' CONFIG_DAYNAPORT
+  fi
+  tristate 'Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)' CONFIG_MACSONIC
+  tristate 'Macintosh SMC 9194 based ethernet cards' CONFIG_SMC9194
+  tristate 'Macintosh CS89x0 based ethernet cards' CONFIG_MAC89x0
+  bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE
+
 fi
 if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then
   tristate 'MVME147 (Lance) Ethernet support' CONFIG_MVME147_NET
@@ -336,7 +349,7 @@
   tristate 'Atari mouse support' CONFIG_ATARIMOUSE
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-   bool 'Mac ADB mouse support' CONFIG_ADBMOUSE
+   bool 'Macintosh ADB mouse support' CONFIG_ADBMOUSE
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
@@ -361,10 +374,13 @@
     dep_tristate 'GVP IO-Extender parallel printer support' CONFIG_GVPIOEXT_LP $CONFIG_GVPIOEXT
     dep_tristate 'GVP IO-Extender PLIP support' CONFIG_GVPIOEXT_PLIP $CONFIG_GVPIOEXT
     tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool 'HyperCom1 serial support (EXPERIMENTAL)' CONFIG_HYPERCOM1
+    fi
   fi
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-  bool 'Mac SCC serial support' CONFIG_MAC_SCC
+  bool 'Macintosh SCC serial support' CONFIG_MAC_SCC
 fi
 if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then
   tristate 'HP DCA serial support' CONFIG_HPDCA
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/bindec.S linux/arch/m68k/fpsp040/bindec.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/bindec.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/bindec.S	Mon Dec  3 20:33:54 2001
@@ -484,7 +484,7 @@
 	fmovex	(%a0),%fp0	|load X from memory
 	fabsx	%fp0		|use abs(X)
 	tstw	%d5		|LAMBDA is in lower word of d5
-	bnes	sc_mul		|if neg (LAMBDA = 1), scale by mul
+	jne	sc_mul		|if neg (LAMBDA = 1), scale by mul
 	fdivx	%fp1,%fp0		|calculate X / SCALE -> Y to fp0
 	bras	A10_st		|branch to A10
 
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/decbin.S linux/arch/m68k/fpsp040/decbin.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/decbin.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/decbin.S	Mon Dec  3 20:33:54 2001
@@ -230,7 +230,7 @@
 |
 m_sign:
 	btst	#31,(%a0)	|test sign of the mantissa
-	beqs	ap_st_z		|if clear, go to append/strip zeros
+	jeq	ap_st_z		|if clear, go to append/strip zeros
 	fnegx	%fp0		|if set, negate fp0
 	
 |
@@ -288,7 +288,7 @@
 	cmpl	#27,%d1		|test is with 27
 	ble	pwrten		|if abs(expA) <28, skip ap/st zeros
 	btst	#30,(%a0)	|check sign of exp
-	bnes	ap_st_n		|if neg, go to neg side
+	jne	ap_st_n		|if neg, go to neg side
 	clrl	%d1		|zero count reg
 	movel	(%a0),%d4		|load lword 1 to d4
 	bfextu	%d4{#28:#4},%d0	|get M16 in d0
@@ -336,7 +336,7 @@
 	tstl	%d0		|check if d0 is zero
 	bnes	ap_p_el		|if not, get next bit
 	fmulx	%fp1,%fp0		|mul mantissa by 10**(no_bits_shifted)
-	bras	pwrten		|go calc pwrten
+	jra	pwrten		|go calc pwrten
 |
 | This section handles a negative adjusted exponent.
 |
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/do_func.S linux/arch/m68k/fpsp040/do_func.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/do_func.S	Tue Apr 23 21:31:24 1996
+++ linux/arch/m68k/fpsp040/do_func.S	Mon Dec  3 20:33:54 2001
@@ -77,7 +77,7 @@
 	movew	CMDREG1B(%a6),%d0
 	andl	#0x7F,%d0
 	cmpil	#0x38,%d0		|if the extension is >= $38, 
-	bges	serror		|it is illegal
+	jge	serror		|it is illegal
 	bfextu	STAG(%a6){#0:#3},%d1
 	lsll	#3,%d0		|make room for STAG
 	addl	%d1,%d0		|combine for final index into table
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/get_op.S linux/arch/m68k/fpsp040/get_op.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/get_op.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/get_op.S	Mon Dec  3 20:33:54 2001
@@ -171,7 +171,7 @@
 get_op:
 	clrb	DY_MO_FLG(%a6)
 	tstb	UFLG_TMP(%a6)	|test flag for unsupp/unimp state
-	beqs	uni_getop
+	jeq	uni_getop
 
 uns_getop:
 	btstb	#direction_bit,CMDREG1B(%a6)
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/skeleton.S linux/arch/m68k/fpsp040/skeleton.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/skeleton.S	Tue Jan 19 10:58:26 1999
+++ linux/arch/m68k/fpsp040/skeleton.S	Mon Dec  3 20:33:54 2001
@@ -107,7 +107,7 @@
 	bnes		not_fmt40
 	fmovel		%fpsr,-(%sp)
 	btstb		#E1,E_BYTE(%a6)		|test for E1 set
-	beqs		not_b1232
+	beq		not_b1232
 	btstb		#snan_bit,2(%sp) |test for snan
 	beq		inex_ckofl
 	addl		#4,%sp
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/util.S linux/arch/m68k/fpsp040/util.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/util.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/util.S	Mon Dec  3 20:33:54 2001
@@ -138,20 +138,20 @@
 |
 ovf_fsgl:
 	clrl	%d0
-	bras	ovf_res
+	jra	ovf_res
 
 ovff_sgl:
 	movel	#0x00000001,%d0		|set single
-	bras	ovf_res
+	jra	ovf_res
 ovff_dbl:
 	movel	#0x00000002,%d0		|set double
-	bras	ovf_res
+	jra	ovf_res
 |
 | The precision is in the fpcr.
 |
 ovf_fpcr:
 	bfextu	FPCR_MODE(%a6){#0:#2},%d0 |set round precision
-	bras	ovf_res
+	jra	ovf_res
 	
 |
 |
diff -urN linux-2.2.18-pre15/arch/m68k/fpsp040/x_store.S linux/arch/m68k/fpsp040/x_store.S
--- linux-2.2.18-pre15/arch/m68k/fpsp040/x_store.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/x_store.S	Mon Dec  3 20:33:54 2001
@@ -92,7 +92,7 @@
 	cmpil	#0,%d0		|if dest format is extended
 	beq	dest_ext	|then branch
 	cmpil	#1,%d0		|if dest format is single
-	beqs	dest_sgl	|then branch
+	jeq	dest_sgl	|then branch
 |
 |	fall through to dest_dbl
 |
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S
--- linux-2.2.18-pre15/arch/m68k/kernel/entry.S	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/entry.S	Mon Dec  3 20:33:54 2001
@@ -90,8 +90,18 @@
 	jbsr	SYMBOL_NAME(syscall_trace)
 
 SYMBOL_NAME_LABEL(ret_from_signal)
-	RESTORE_SWITCH_STACK
+	RESTORE_SWITCH_STACK 
+	addql	#4,%sp
+/* on 68040 complete pending writebacks if any */	
+#ifdef CONFIG_M68040
+	bfextu	%sp@(PT_VECTOR){#0,#4},%d0  
+	subql	#7,%d0				| bus error frame ?
+	jbne	1f
+	movel	%sp,%sp@-
+	jbsr	SYMBOL_NAME(berr_040cleanup)
 	addql	#4,%sp
+1:	
+#endif	
 	jra	SYMBOL_NAME(ret_from_exception)
 
 ENTRY(system_call)
@@ -283,6 +293,11 @@
 	movel	%sp,%a0@(TASK_TSS+TSS_KSP)
 
 	/* save floating point context */
+#ifndef CONFIG_FPU_EMU_ONLY
+#ifdef CONFIG_FPU_EMU
+	tstl	SYMBOL_NAME(m68k_fputype)
+	jeq	3f
+#endif
 	fsave	%a0@(TASK_TSS+TSS_FPSTATE)
 
 #if defined(CONFIG_M68060)
@@ -304,6 +319,7 @@
 2:	fmovemx	%fp0-%fp7,%a0@(TASK_TSS+TSS_FPREG)
 	fmoveml	%fpcr/%fpsr/%fpiar,%a0@(TASK_TSS+TSS_FPCNTL)
 3:
+#endif	/* CONFIG_FPU_EMU_ONLY */
 	/* Return previous task in %d1 */
 	movel	%curptr,%d1
 
@@ -374,7 +390,11 @@
 2:
 4:
 	/* restore floating point context */
-
+#ifndef CONFIG_FPU_EMU_ONLY
+#ifdef CONFIG_FPU_EMU
+	tstl	SYMBOL_NAME(m68k_fputype)
+	jeq	4f
+#endif
 #if defined(CONFIG_M68060)
 #if !defined(CPU_M68060_ONLY)
 	btst	#3,SYMBOL_NAME(m68k_cputype)+3
@@ -394,6 +414,8 @@
 2:	fmovemx	%a1@(TASK_TSS+TSS_FPREG),%fp0-%fp7
 	fmoveml	%a1@(TASK_TSS+TSS_FPCNTL),%fpcr/%fpsr/%fpiar
 3:	frestore %a1@(TASK_TSS+TSS_FPSTATE)
+4:
+#endif	/* CONFIG_FPU_EMU_ONLY */
 
 	/* restore the kernel stack pointer */
 	movel	%a1@(TASK_TSS+TSS_KSP),%sp
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/head.S linux/arch/m68k/kernel/head.S
--- linux-2.2.18-pre15/arch/m68k/kernel/head.S	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/head.S	Mon Dec  3 20:33:54 2001
@@ -130,7 +130,7 @@
  *
  * mmu_engage
  * ----------
- *	Thanks to a small helping routine enabling the mmu got quiet simple
+ *	Thanks to a small helping routine enabling the mmu got quite simple
  * and there is only one way left. mmu_engage makes a complete a new mapping
  * that only includes the absolute necessary to be able to jump to the final
  * postion and to restore the original mapping.
@@ -271,6 +271,7 @@
  */
 
 #define CONSOLE
+#define CONSOLE_PENGUIN
 
 /*
  * Macintosh serial debug support; outputs boot info to the printer
@@ -305,7 +306,7 @@
 .globl SYMBOL_NAME(m68k_pgtable_cachemode)
 .globl SYMBOL_NAME(m68k_supervisor_cachemode)
 #ifdef CONFIG_MVME16x
-.globl SYMBOL_NAME(mvme_bdid_ptr)
+.globl SYMBOL_NAME(mvme_bdid)
 #endif
 #ifdef CONFIG_Q40
 .globl SYMBOL_NAME(q40_mem_cptr)	
@@ -386,6 +387,17 @@
 #endif
 #endif
 
+/* A macro to make relocating pointers read from memory structures easier.
+ * Remember, we're not necessarily running where the linker thinks we are,
+ * yet, so pointers given by the linker must be manually relocated to be
+ * valid. [CSA 13-May-1999]
+ */
+.macro	load_and_relocate	reference,dest
+0:	lea	%pc@(0b),\dest
+	subl	#0b,\dest
+	addl	\reference,\dest
+.endm
+
 /* Several macros to make the writing of subroutines easier:
  * - func_start marks the beginning of the routine which setups the frame
  *   register and saves the registers, it also defines another macro
@@ -472,6 +484,12 @@
 func_define	serial_putc,1
 func_define	console_putc,1
 
+func_define	console_init
+func_define	console_put_stats
+func_define	console_put_penguin
+func_define	console_plot_pixel,3
+func_define	console_scroll
+
 .macro	putc	ch
 #if defined(CONSOLE) || defined(SERIAL_DEBUG)
 	pea	\ch
@@ -503,10 +521,18 @@
 
 .macro	puts		string
 #if defined(CONSOLE) || defined(SERIAL_DEBUG)
+/* The __INITDATA stuff is a no-op when ftrace or kgdb are turned on */
+#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB)
+	bra 1f
+#endif
 	__INITDATA
 .Lstr\@:
 	.string	"\string"
 	__FINIT
+#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB)
+	.align 2
+1:
+#endif
 	pea	%pc@(.Lstr\@)
 	func_call	puts
 	addql	#4,%sp
@@ -525,6 +551,9 @@
 #define is_not_mvme147(lab) cmpl &MACH_MVME147,%pc@(m68k_machtype); jne lab
 #define is_not_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jne lab
 #define is_not_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jne lab
+#define is_mvme147(lab) cmpl &MACH_MVME147,%pc@(m68k_machtype); jeq lab
+#define is_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jeq lab
+#define is_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jeq lab
 #define is_not_hp300(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); jne lab
 #define is_not_q40(lab) cmpl &MACH_Q40,%pc@(m68k_machtype); jne lab
 #define is_not_sun3x(lab) cmpl &MACH_SUN3X,%pc@(m68k_machtype); jne lab
@@ -801,6 +830,57 @@
 L(notypetest):
 #endif
 
+#ifdef CONFIG_VME
+	is_mvme147(L(getvmetype))
+	is_bvme6000(L(getvmetype))
+	is_not_mvme16x(L(gvtdone))
+
+	/* See if the loader has specified the BI_VME_TYPE tag.  Recent
+	 * versions of VMELILO and TFTPLILO do this.  We have to do this
+	 * early so we know how to handle console output.  If the tag
+	 * doesn't exist then we use the Bug for output on MVME16x.
+	 */
+L(getvmetype):
+	get_bi_record	BI_VME_TYPE
+	tstl	%d0
+	jbmi	1f
+	movel	%a0@,%d3
+	lea	%pc@(SYMBOL_NAME(vme_brdtype)),%a0
+	movel	%d3,%a0@
+1:
+#ifdef CONFIG_MVME16x
+	is_not_mvme16x(L(gvtdone))
+
+	/* Need to get the BRD_ID info to diferentiate between 162, 167,
+	 * etc.  This is available as a BI_VME_BRDINFO tag with later
+	 * versions of VMELILO and TFTPLILO, otherwise we call the Bug.
+	 */
+	get_bi_record	BI_VME_BRDINFO
+	tstl	%d0
+	jpl	1f
+
+	/* Get pointer to board ID data from Bug */
+	movel	%d2,%sp@-
+	trap	#15
+	.word	0x70		/* trap 0x70 - .BRD_ID */
+	movel	%sp@+,%a0
+1:
+	lea	%pc@(SYMBOL_NAME(mvme_bdid)),%a1
+	/* Structure is 32 bytes long */
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+	movel	%a0@+,%a1@+
+#endif
+
+L(gvtdone):
+
+#endif
+
 /*
  * Initialize serial port
  */
@@ -812,11 +892,11 @@
 #ifdef CONFIG_MAC
 	is_not_mac(L(nocon))
 #ifdef CONSOLE
-	jbsr	L(console_init)
+	console_init
 #ifdef CONSOLE_PENGUIN
-	jbsr	L(console_put_penguin)
+	console_put_penguin
 #endif	/* CONSOLE_PENGUIN */
-	jbsr	L(console_put_stats)
+	console_put_stats
 #endif	/* CONSOLE */
 L(nocon):
 #endif	/* CONFIG_MAC */
@@ -998,14 +1078,6 @@
 
 	is_not_mvme16x(L(not16x))
 
-	/* Get pointer to board ID data */
-	movel	%d2,%sp@-
-	trap	#15
-	.word	0x70		/* trap 0x70 - .BRD_ID */
-	movel	%sp@+,%d2
-	lea	%pc@(SYMBOL_NAME(mvme_bdid_ptr)),%a0
-	movel	%d2,%a0@
-
 	/*
 	 * On MVME16x we have already created kernel page tables for
 	 * 4MB of RAM at address 0, so now need to do a transparent
@@ -1082,10 +1154,6 @@
 
 	putc	'F'
 
-	lea	%pc@(L(mac_videobase)),%a0
-	lea	%pc@(L(console_video_virtual)),%a1
-	movel	%a0@,%a1@
-
 	is_not_040_or_060(1f)
 
 	moveq	#_PAGE_NOCACHE_S,%d3
@@ -1099,15 +1167,16 @@
 	 */
 
 	movel	#VIDEOMEMMASK,%d0
-	andl	L(mac_videobase),%d0
+	andl	%pc@(L(mac_videobase)),%d0
 
 	mmu_map		#VIDEOMEMBASE,%d0,#VIDEOMEMSIZE,%d3
-	mmu_map_eq	#0x40800000,#0x02000000,%d3	/* rom ? */
-	mmu_map_eq	#0x50000000,#0x02000000,%d3
-	mmu_map_eq	#0x60000000,#0x00400000,%d3
-	mmu_map_eq	#0x9c000000,#0x00400000,%d3
+	/* IO devices (incl. serial port) from 5000 0000 to 5300 0000 */
+	mmu_map_eq	#0x50000000,#0x03000000,%d3
+	/* Nubus slot space (video at 0xF0000000, rom at 0xF0F80000) */
 	mmu_map_tt	#1,#0xf8000000,#0x08000000,%d3
-
+	/* ROM from 4000 0000 to 4200 0000 */
+	mmu_map_eq	#0x40000000,#0x02000000,%d3 /* only for mac_reset() */
+	
 	jbra	L(mmu_init_done)
 
 L(mmu_init_not_mac):
@@ -1261,6 +1330,9 @@
 	andl	L(mac_videobase),%d0
 	addl	#VIDEOMEMBASE,%d0
 	movel	%d0,L(mac_videobase)
+#ifdef MAC_SERIAL_DEBUG
+	orl	#0x50000000,L(mac_sccbase)
+#endif
 1:
 #endif
 
@@ -1661,7 +1733,7 @@
 	putZc('C','I')	/* write through or copy-back */
 	rts
 
-mmu_030_print:
+mmu_030_print: /* print a 3-level (max) translation tree using 3 nested loops*/
 	puts	"\nMMU030\n"
 	puts	"\nrp:"
 	putn	%a5
@@ -1670,62 +1742,66 @@
 	andil	#0xfffffff0,%d0
 	movel	%d0,%a0
 	movel	#0x00000000,%a4		/* logical address */
-	movel	#0,%d0
+	movel	#0,%d0	/* iteration counter for outer loop */
 30:
 	movel	%a4,%d5
-	addil	#PAGESIZE<<13,%d5
+	addil	#PAGESIZE<<13,%d5	/* next logical address */
 	movel	%a0@+,%d6
-	btst	#1,%d6			/* is it a ptr? */
-	jbne	31f			/* yes */
+	btst	#1,%d6			/* is it a table ptr? */
+	jbne	31f			/* if yes, jump */
 	btst	#0,%d6			/* is it early terminating? */
-	jbeq	1f			/* no */
-	jbsr	mmu_030_print_helper
+	jbeq	1f			/* if no, jump */
+	/* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) (early termination) */
+	jbsr	mmu_030_print_helper	/* print " %a4-> %d6"+flags(%d6) */
 	jbra	38f
-1:
-	jbsr	mmu_print_tuple_invalidate
+1:	/* DT (descriptor tag) = $00 (INVALID) */
+	jbsr	mmu_print_tuple_invalidate /* prints " %a4##" */
 	jbra	38f
-31:
-	movel	#0,%d1
+31:	/* DT (descriptor tag) = $1x (table pointer with 4 or 8 byte entries)*/
+	movel	#0,%d1	/* iteration counter for middle loop */
 	andil	#0xfffffff0,%d6
 	movel	%d6,%a1
 32:
 	movel	%a4,%d5
 	addil	#PAGESIZE<<6,%d5
 	movel	%a1@+,%d6
-	btst	#1,%d6
-	jbne	33f
-	btst	#0,%d6
-	jbeq	1f			/* no */
-	jbsr	mmu_030_print_helper
+	btst	#1,%d6			/* is it a table ptr? */
+	jbne	33f			/* if so, jump */
+	btst	#0,%d6			/* is it a page descriptor? */
+	jbeq	1f			/* if no, jump */
+	/* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) */
+	jbsr	mmu_030_print_helper	/* prints " %a4-> %d6"+flags(%d6) */
 	jbra	37f
-1:
-	jbsr	mmu_print_tuple_invalidate
+1:	/* DT (descriptor tag) = $00 (INVALID) */
+	jbsr	mmu_print_tuple_invalidate /* prints " %a4##" */
 	jbra	37f
-33:
-	movel	#0,%d2
+33:	/* DT (descriptor tag) = $1x (table pointer with 4 or 8 byte entries)*/
+	movel	#0,%d2	/* iteration counter for inner loop */
 	andil	#0xfffffff0,%d6
 	movel	%d6,%a2
 34:
 	movel	%a4,%d5
 	addil	#PAGESIZE,%d5
 	movel	%a2@+,%d6
+	/* note that we assume there are no more table indirections left */
 	btst	#0,%d6
 	jbne	35f
-	jbsr	mmu_print_tuple_invalidate
+	/* DT (descriptor tag) = $00 (INVALID) */
+	jbsr	mmu_print_tuple_invalidate /* prints " %a4##" */
 	jbra	36f
-35:
-	jbsr	mmu_030_print_helper
-36:
+35:	/* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) */
+	jbsr	mmu_030_print_helper	/* prints " %a4-> %d6"+flags(%d6) */
+36:	/* continue inner loop */
 	movel	%d5,%a4
 	addq	#1,%d2
 	cmpib	#64,%d2
 	jbne	34b
-37:
-	movel	%d5,%a4
+37:	/* continue middle loop */
+	movel	%d5,%a4			/* move to the next logical address */
 	addq	#1,%d1
 	cmpib	#128,%d1
 	jbne	32b
-38:
+38:	/* continue outer loop */
 	movel	%d5,%a4			/* move to the next logical address */
 	addq	#1,%d0
 	cmpib	#128,%d0
@@ -1746,6 +1822,13 @@
 	moveml	%sp@+,%d0-%d1
 	rts
 
+/* mmu_print_tuple_invalidate(%a4=this_logical) {
+ *	if (mmu_print_data.mmu_next_valid != INVALID) {
+ *	    mmu_print_data.mmu_next_valid =  INVALID;
+ *	    printf(" %08x##\n", this_logical);
+ *	}
+ * } [rough C translation by CSA, not the original author]
+ */
 mmu_print_tuple_invalidate:
 	moveml	%a0/%d7,%sp@-
 
@@ -1764,6 +1847,19 @@
 	rts
 
 
+/* mmu_print_tuple(%d0=logical,%d1=physical,
+ *		   %d5=next_logical,%a4=this_logical,
+ *		   %a6=print_flags) {
+ *	if (mmu_print_data.mmu_next_valid != VALID ||
+ *	    mmu_print_data.mmu_next_physical != physical) {
+ *	    printf(" %08x-> %08x", logical, physical);
+ *	    print_flags(physical);
+ *	    mmu_print_data.mmu_next_valid =  VALID;
+ *	    mmu_print_data.mmu_next_physical=physical;
+ *	}
+ *	mmu_print_data.mmu_next_physical += (next_logical-this_logical);
+ * } [rough C translation by CSA, not the original author]
+ */
 mmu_print_tuple:
 	moveml	%d0-%d7/%a0,%sp@-
 
@@ -2168,7 +2264,7 @@
 func_return	mmu_fixup_page_mmu_cache
 
 /*
- *	mmu_temp_map
+ *	mmu_temp_map (ARG1=physical_addr, ARG2=logical_addr)
  *
  *	create a temporary mapping to enable the mmu,
  *	this we don't need any transparation translation tricks.
@@ -2834,10 +2930,6 @@
 #ifdef CONFIG_MAC
 	is_not_mac(5f)
 
-#ifdef CONSOLE
-	console_putc	%d0
-#endif /* CONSOLE */
-
 #ifdef MAC_SERIAL_DEBUG
 
 #ifdef MAC_USE_SCC_A
@@ -2903,13 +2995,54 @@
 #ifdef CONFIG_MVME16x
 	is_not_mvme16x(2f)
 	/*
-	 * The VME 16x class has PROM support for serial output
-	 * of some kind;  the TRAP table is still valid.
+	 * If the loader gave us a board type then we can use that to
+	 * select an appropriate output routine; otherwise we just use
+	 * the Bug code.  If we haev to use the Bug that means the Bug
+	 * workspace has to be valid, which means the Bug has to use
+	 * the SRAM, which is non-standard.
 	 */
 	moveml	%d0-%d7/%a2-%a6,%sp@-
+	movel	SYMBOL_NAME(vme_brdtype),%d1
+	jeq	1f			| No tag - use the Bug
+	cmpi	#VME_TYPE_MVME162,%d1
+	jeq	6f
+	cmpi	#VME_TYPE_MVME172,%d1
+	jne	5f
+	/* 162/172; it's an SCC */
+6:	btst	#2,M162_SCC_CTRL_A
+	nop
+	nop
+	nop
+	jeq	6b
+	moveb	#8,M162_SCC_CTRL_A
+	nop
+	nop
+	nop
+	moveb	%d0,M162_SCC_CTRL_A
+	jra	3f
+5:
+	/* 166/167/177; its a CD2401 */
+	moveb	#0,M167_CYCAR
+	moveb	M167_CYIER,%d2
+	moveb	#0x02,M167_CYIER
+7:
+	btst	#5,M167_PCSCCTICR
+	jeq	7b
+	moveb	M167_PCTPIACKR,%d1
+	moveb	M167_CYLICR,%d1
+	jeq	8f
+	moveb	#0x08,M167_CYTEOIR
+	jra	7b
+8:
+	moveb	%d0,M167_CYTDR
+	moveb	#0,M167_CYTEOIR
+	moveb	%d2,M167_CYIER
+	jra	3f
+1:
 	moveb	%d0,%sp@-
 	trap	#15
 	.word	0x0020	/* TRAP 0x020 */
+3:
 	moveml	%sp@+,%d0-%d7/%a2-%a6
 	jbra	L(serial_putc_done)
 2:
@@ -3004,20 +3137,18 @@
  *	simple strings!
  */
 ENTRY(mac_serial_print)
+#ifdef MAC_SERIAL_DEBUG
 	moveml	%d0/%a0,%sp@-
-#if 1
 	move	%sr,%sp@-
 	ori	#0x0700,%sr
-#endif
-	movel	%sp@(10),%a0		/* fetch parameter */
+	movel	%sp@(14),%a0		/* fetch parameter */
 	jra	2f
 1:	serial_putc	%d0
 2:	moveb	%a0@+,%d0
 	jne	1b
-#if 1
 	move	%sp@+,%sr
-#endif
 	moveml	%sp@+,%d0/%a0
+#endif /* MAC_SERIAL_DEBUG */
 	rts
 #endif /* CONFIG_MAC */
 
@@ -3041,7 +3172,7 @@
 #define Lconsole_struct_left_edge	16
 #define Lconsole_struct_penguin_putc	20
 
-L(console_init):
+func_start	console_init,%a0-%a4/%d0-%d7
 	/*
 	 *	Some of the register usage that follows
 	 *		a0 = pointer to boot_info
@@ -3055,49 +3186,57 @@
 	 *		d5 = number of bytes per scan line
 	 *		d6 = number of bytes on the entire screen
 	 */
-	moveml	%a0-%a4/%d0-%d7,%sp@-
 
 	lea	%pc@(L(console_globals)),%a2
-	lea	%pc@(L(mac_videobase)),%a0
-	movel	%a0@,%a1
-	lea	%pc@(L(mac_rowbytes)),%a0
-	movel	%a0@,%d5
-	lea	%pc@(L(mac_dimensions)),%a0
-	movel	%a0@,%d3	/* -> low byte */
+	movel	%pc@(L(mac_videobase)),%a1
+	movel	%pc@(L(mac_rowbytes)),%d5
+	movel	%pc@(L(mac_dimensions)),%d3	/* -> low word */
 	movel	%d3,%d4
-	swap	%d4		/* -> high byte */
-	andl	#0xffff,%d3	/* d3 = screen width in pixels */
-	andl	#0xffff,%d4	/* d4 = screen height in pixels */
+	swap	%d4				/* -> high word */
+	andl	#0xffff,%d3			/* d3 = screen width in pixels */
+	andl	#0xffff,%d4			/* d4 = screen height in pixels */
 
 	movel	%d5,%d6
-	subl	#20,%d6
-	mulul	%d4,%d6		/* scan line bytes x num scan lines */
-	divul	#8,%d6		/* we'll clear 8 bytes at a time */
+|	subl	#20,%d6
+	mulul	%d4,%d6				/* scan line bytes x num scan lines */
+	lsrl	#3,%d6				/* we'll clear 8 bytes at a time */
+	moveq	#-1,%d0				/* Mac_black */
 	subq	#1,%d6
 
-console_clear_loop:
-	movel	#0xffffffff,%a1@+	/* Mac_black */
-	movel	#0xffffffff,%a1@+	/* Mac_black */
-	dbra	%d6,console_clear_loop
+L(console_clear_loop):
+	movel	%d0,%a1@+
+	movel	%d0,%a1@+
+	dbra	%d6,L(console_clear_loop)
 
 	/* Calculate font size */
-
+	/* Use absolute (not pc-relative) pointer here; which means we'll
+	 * have to do relocation every time before we use it.
+	 * (remember, before mmu_engage we're probably not running where
+	 * the linker expected that we'd end up.)  We could store
+	 * %pc@(SYMBOL_NAME(font_xxx)) in memory instead, but if 
+	 * everyone did this we'd end up with quite a motley collection of
+	 * fixup code after mmu_engage, which offends our spirit of 
+	 * disentangledness (code for similar things together, code for
+	 * unrelated things apart).
+	 */
+	
 #if   defined(FONT_8x8)
-	lea	%pc@(SYMBOL_NAME(font_vga_8x8)), %a0
+	lea	SYMBOL_NAME(font_vga_8x8), %a0
 #elif defined(FONT_8x16)
-	lea	%pc@(SYMBOL_NAME(font_vga_8x16)),%a0
+	lea	SYMBOL_NAME(font_vga_8x16),%a0
 #elif defined(FONT_6x11)
-	lea	%pc@(SYMBOL_NAME(font_vga_6x11)),%a0
+	lea	SYMBOL_NAME(font_vga_6x11),%a0
 #else	/*   (FONT_8x8) default */
-	lea	%pc@(SYMBOL_NAME(font_vga_8x8)), %a0
+	lea	SYMBOL_NAME(font_vga_8x8), %a0
 #endif
 
 	/*
 	 *	At this point we make a shift in register usage
-	 *	a1 = address of Lconsole_font pointer
+	 *	a1 = address of console_font pointer
 	 */
 	lea	%pc@(L(console_font)),%a1
-	movel	%a0,%a1@	/* store pointer to struct fbcon_font_desc in Lconsole_font */
+	movel	%a0,%a1@	/* store pointer to struct fbcon_font_desc in console_font */
+	load_and_relocate %a1@,%a0	/* now relocate the pointer. */
 
 	/*
 	 *	Calculate global maxs
@@ -3106,11 +3245,11 @@
 	 *	6 x 11 also supported
 	 */
 		/* ASSERT: a0 = contents of Lconsole_font */
-	movel	%d3,%d0			/* screen width in pixels */
-	divul	%a0@(FBCON_FONT_DESC_WIDTH),%d0		/* d0 = max num chars per row */
+	movel	%d3,%d0				/* screen width in pixels */
+	divul	%a0@(FBCON_FONT_DESC_WIDTH),%d0	/* d0 = max num chars per row */
 
-	movel	%d4,%d1			 /* screen height in pixels */
-	divul	%a0@(FBCON_FONT_DESC_HEIGHT),%d1	 /* d1 = max num rows */
+	movel	%d4,%d1				 /* screen height in pixels */
+	divul	%a0@(FBCON_FONT_DESC_HEIGHT),%d1 /* d1 = max num rows */
 
 	movel	%d0,%a2@(Lconsole_struct_num_columns)
 	movel	%d1,%a2@(Lconsole_struct_num_rows)
@@ -3125,16 +3264,14 @@
 	/*
 	 * Initialization is complete
 	 */
-	moveml	%sp@+,%a0-%a4/%d0-%d7
-	rts
+func_return	console_init
 
-L(console_put_stats):
+func_start	console_put_stats,%a0/%d7
 	/*
 	 *	Some of the register usage that follows
 	 *		a0 = pointer to boot_info
 	 *		d7 = value of boot_info fields
 	 */
-	moveml	%a0/%d7,%sp@-
 
 	puts	"\nMacLinux\n\n"
 
@@ -3154,21 +3291,28 @@
 	putn	%pc@(L(cputype))
 	putc	'\n'
 
+	putn	%pc@(L(mac_videodepth))
+	putn	%pc@(L(mac_dimensions))
+	putn	%pc@(L(mac_rowbytes))
+	putn	%pc@(L(mac_videodepth))
+#ifdef MAC_SERIAL_DEBUG
+	putn	%pc@(L(mac_sccbase))
+#endif
+	putc	'\n'
+
 #  if defined(MMU_PRINT)
 	jbsr	mmu_print_machine_cpu_types
 #  endif /* MMU_PRINT */
 #endif /* SERIAL_DEBUG */
 
-	moveml	%sp@+,%a0/%d7
-	rts
+func_return	console_put_stats
 
 #ifdef CONSOLE_PENGUIN
-L(console_put_penguin):
+func_start	console_put_penguin,%a0-%a1/%d0-%d7
 	/*
 	 *	Get 'that_penguin' onto the screen in the upper right corner
 	 *	penguin is 64 x 74 pixels, align against right edge of screen
 	 */
-	moveml	%a0-%a1/%d0-%d7,%sp@-
 
 	lea	%pc@(L(mac_dimensions)),%a0
 	movel	%a0@,%d0
@@ -3177,40 +3321,42 @@
 	clrl	%d1		/* start at the top */
 	movel	#73,%d7
 	lea	%pc@(SYMBOL_NAME(that_penguin)),%a1
-console_penguin_row:
+L(console_penguin_row):
 	movel	#31,%d6
-console_penguin_pixel_pair:
+L(console_penguin_pixel_pair):
 	moveb	%a1@,%d2
 	lsrb	#4,%d2
-	jbsr	console_plot_pixel
+	console_plot_pixel %d0,%d1,%d2
 	addq	#1,%d0
 	moveb	%a1@+,%d2
-	jbsr	console_plot_pixel
+	console_plot_pixel %d0,%d1,%d2
 	addq	#1,%d0
-	dbra	%d6,console_penguin_pixel_pair
+	dbra	%d6,L(console_penguin_pixel_pair)
 
 	subil	#64,%d0
 	addq	#1,%d1
-	dbra	%d7,console_penguin_row
+	dbra	%d7,L(console_penguin_row)
 
-	moveml	%sp@+,%a0-%a1/%d0-%d7
-	rts
-#endif
+func_return	console_put_penguin
 
-console_scroll:
-	moveml	%a0-%a4/%d0-%d7,%sp@-
+/* include penguin bitmap */
+SYMBOL_NAME_LABEL(that_penguin)
+#include "../mac/mac_penguin.S"	
+#endif
 
 	/*
 	 * Calculate source and destination addresses
 	 *	output	a1 = dest
 	 *		a2 = source
 	 */
+
+func_start	console_scroll,%a0-%a4/%d0-%d7
 	lea	%pc@(L(mac_videobase)),%a0
 	movel	%a0@,%a1
 	movel	%a1,%a2
 	lea	%pc@(L(mac_rowbytes)),%a0
 	movel	%a0@,%d5
-	movel	%pc@(L(console_font)),%a0
+	load_and_relocate %pc@(L(console_font)),%a0
 	mulul	%a0@(FBCON_FONT_DESC_HEIGHT),%d5	/* account for # scan lines per character */
 	addal	%d5,%a2
 
@@ -3229,13 +3375,13 @@
 	 */
 	lea	%pc@(L(mac_rowbytes)),%a0
 	movel	%a0@,%d6
-	movel	%pc@(L(console_font)),%a0
+	load_and_relocate %pc@(L(console_font)),%a0
 	subl	%a0@(FBCON_FONT_DESC_HEIGHT),%d4	/* we're not scrolling the top row! */
 	mulul	%d4,%d6		/* scan line bytes x num scan lines */
 	divul	#32,%d6		/* we'll move 8 longs at a time */
 	subq	#1,%d6
 
-console_scroll_loop:
+L(console_scroll_loop):
 	movel	%a2@+,%a1@+
 	movel	%a2@+,%a1@+
 	movel	%a2@+,%a1@+
@@ -3244,17 +3390,17 @@
 	movel	%a2@+,%a1@+
 	movel	%a2@+,%a1@+
 	movel	%a2@+,%a1@+
-	dbra	%d6,console_scroll_loop
+	dbra	%d6,L(console_scroll_loop)
 
 	lea	%pc@(L(mac_rowbytes)),%a0
 	movel	%a0@,%d6
-	movel	%pc@(L(console_font)),%a0
+	load_and_relocate %pc@(L(console_font)),%a0
 	mulul	%a0@(FBCON_FONT_DESC_HEIGHT),%d6	/* scan line bytes x font height */
 	divul	#32,%d6			/* we'll move 8 words at a time */
 	subq	#1,%d6
 
 	moveq	#-1,%d0
-console_scroll_clear_loop:
+L(console_scroll_clear_loop):
 	movel	%d0,%a1@+
 	movel	%d0,%a1@+
 	movel	%d0,%a1@+
@@ -3263,15 +3409,13 @@
 	movel	%d0,%a1@+
 	movel	%d0,%a1@+
 	movel	%d0,%a1@+
-	dbra	%d6,console_scroll_clear_loop
-
-	moveml	%sp@+,%a0-%a4/%d0-%d7
-	rts
+	dbra	%d6,L(console_scroll_clear_loop)
 
+func_return	console_scroll
 
 func_start	console_putc,%a0/%a1/%d0-%d7
 
-	is_not_mac(console_exit)
+	is_not_mac(L(console_exit))
 
 	/* Output character in d7 on console.
 	 */
@@ -3285,7 +3429,7 @@
 	lea	%pc@(L(console_globals)),%a0
 
 	cmpib	#10,%d7
-	jne	console_not_lf
+	jne	L(console_not_lf)
 	movel	%a0@(Lconsole_struct_cur_row),%d0
 	addil	#1,%d0
 	movel	%d0,%a0@(Lconsole_struct_cur_row)
@@ -3294,22 +3438,22 @@
 	jcs	1f
 	subil	#1,%d0
 	movel	%d0,%a0@(Lconsole_struct_cur_row)
-	jbsr	console_scroll
+	console_scroll
 1:
-	jra	console_exit
+	jra	L(console_exit)
 
-console_not_lf:
+L(console_not_lf):
 	cmpib	#13,%d7
-	jne	console_not_cr
+	jne	L(console_not_cr)
 	clrl	%a0@(Lconsole_struct_cur_column)
-	jra	console_exit
+	jra	L(console_exit)
 
-console_not_cr:
+L(console_not_cr):
 	cmpib	#1,%d7
-	jne	console_not_home
+	jne	L(console_not_home)
 	clrl	%a0@(Lconsole_struct_cur_row)
 	clrl	%a0@(Lconsole_struct_cur_column)
-	jra	console_exit
+	jra	L(console_exit)
 
 /*
  *	At this point we know that the %d7 character is going to be
@@ -3320,13 +3464,13 @@
  *		d1 = cursor row to draw the character
  *		d7 = character number
  */
-console_not_home:
+L(console_not_home):
 	movel	%a0@(Lconsole_struct_cur_column),%d0
-	addil	#1,%a0@(Lconsole_struct_cur_column)
+	addql	#1,%a0@(Lconsole_struct_cur_column)
 	movel	%a0@(Lconsole_struct_num_columns),%d1
 	cmpl	%d1,%d0
 	jcs	1f
-	putc	'\n'	/* recursion is OK! */
+	console_putc	#'\n'	/* recursion is OK! */
 1:
 	movel	%a0@(Lconsole_struct_cur_row),%d1
 
@@ -3334,8 +3478,8 @@
 	 *	At this point we make a shift in register usage
 	 *	a0 = address of pointer to font data (fbcon_font_desc)
 	 */
-	movel	%pc@(L(console_font)),%a0
-	movel	%a0@(FBCON_FONT_DESC_DATA),%a1	/* Load fbcon_font_desc.data into a1 */
+	load_and_relocate %pc@(L(console_font)),%a0
+	load_and_relocate %a0@(FBCON_FONT_DESC_DATA),%a1
 	andl	#0x000000ff,%d7
 		/* ASSERT: a0 = contents of Lconsole_font */
 	mulul	%a0@(FBCON_FONT_DESC_HEIGHT),%d7	/* d7 = index into font data */
@@ -3350,35 +3494,34 @@
 	 *	d6 = count down for the font's pixel width (8)
 	 *	d7 = count down for the font's pixel count in height
 	 */
+
 		/* ASSERT: a0 = contents of Lconsole_font */
 	mulul	%a0@(FBCON_FONT_DESC_WIDTH),%d0
 	mulul	%a0@(FBCON_FONT_DESC_HEIGHT),%d1
 	movel	%a0@(FBCON_FONT_DESC_HEIGHT),%d7	/* Load fbcon_font_desc.height into d7 */
 	subq	#1,%d7
-console_read_char_scanline:
+L(console_read_char_scanline):
 	moveb	%a1@+,%d3
 
 		/* ASSERT: a0 = contents of Lconsole_font */
 	movel	%a0@(FBCON_FONT_DESC_WIDTH),%d6	/* Load fbcon_font_desc.width into d6 */
 	subql	#1,%d6
 
-console_do_font_scanline:
+L(console_do_font_scanline):
 	lslb	#1,%d3
 	scsb	%d2		/* convert 1 bit into a byte */
-	jbsr	console_plot_pixel
+	console_plot_pixel %d0,%d1,%d2
 	addq	#1,%d0
-	dbra	%d6,console_do_font_scanline
+	dbra	%d6,L(console_do_font_scanline)
 
 		/* ASSERT: a0 = contents of Lconsole_font */
 	subl	%a0@(FBCON_FONT_DESC_WIDTH),%d0
 	addq	#1,%d1
-	dbra	%d7,console_read_char_scanline
-
-console_exit:
+	dbra	%d7,L(console_read_char_scanline)
 
+L(console_exit):
 func_return	console_putc
 
-console_plot_pixel:
 	/*
 	 *	Input:
 	 *		d0 = x coordinate
@@ -3386,14 +3529,14 @@
 	 *		d2 = (bit 0) 1/0 for white/black (!)
 	 *	All registers are preserved
 	 */
-	moveml	%a0-%a1/%d0-%d4,%sp@-
+func_start	console_plot_pixel,%a0-%a1/%d0-%d4
 
-	lea	%pc@(L(mac_videobase)),%a0
-	movel	%a0@,%a1
-	lea	%pc@(L(mac_videodepth)),%a0
-	movel	%a0@,%d3
-	lea	%pc@(L(mac_rowbytes)),%a0
-	mulul	%a0@,%d1
+	movel	%pc@(L(mac_videobase)),%a1
+	movel	%pc@(L(mac_videodepth)),%d3
+	movel	ARG1,%d0
+	movel	ARG2,%d1
+	mulul	%pc@(L(mac_rowbytes)),%d1
+	movel	ARG3,%d2
 
 	/*
 	 *	Register usage:
@@ -3402,13 +3545,10 @@
 	 *		d2 = black or white (0/1)
 	 *		d3 = video depth
 	 *		d4 = temp of x (d0) for many bit depths
-	 *		d5 = unused
-	 *		d6 = unused
-	 *		d7 = unused
 	 */
-test_1bit:
+L(test_1bit):
 	cmpb	#1,%d3
-	jbne	test_2bit
+	jbne	L(test_2bit)
 	movel	%d0,%d4		/* we need the low order 3 bits! */
 	divul	#8,%d0
 	addal	%d0,%a1
@@ -3416,16 +3556,16 @@
 	andb	#7,%d4
 	eorb	#7,%d4		/* reverse the x-coordinate w/ screen-bit # */
 	andb	#1,%d2
-	jbne	white_1
+	jbne	L(white_1)
 	bsetb	%d4,%a1@
-	jbra	console_plot_pixel_exit
-white_1:
+	jbra	L(console_plot_pixel_exit)
+L(white_1):
 	bclrb	%d4,%a1@
-	jbra	console_plot_pixel_exit
+	jbra	L(console_plot_pixel_exit)
 
-test_2bit:
+L(test_2bit):
 	cmpb	#2,%d3
-	jbne	test_4bit
+	jbne	L(test_4bit)
 	movel	%d0,%d4		/* we need the low order 2 bits! */
 	divul	#4,%d0
 	addal	%d0,%a1
@@ -3434,20 +3574,20 @@
 	eorb	#3,%d4		/* reverse the x-coordinate w/ screen-bit # */
 	lsll	#1,%d4		/* ! */
 	andb	#1,%d2
-	jbne	white_2
+	jbne	L(white_2)
 	bsetb	%d4,%a1@
 	addq	#1,%d4
 	bsetb	%d4,%a1@
-	jbra	console_plot_pixel_exit
-white_2:
+	jbra	L(console_plot_pixel_exit)
+L(white_2):
 	bclrb	%d4,%a1@
 	addq	#1,%d4
 	bclrb	%d4,%a1@
-	jbra	console_plot_pixel_exit
+	jbra	L(console_plot_pixel_exit)
 
-test_4bit:
+L(test_4bit):
 	cmpb	#4,%d3
-	jbne	test_8bit
+	jbne	L(test_8bit)
 	movel	%d0,%d4		/* we need the low order bit! */
 	divul	#2,%d0
 	addal	%d0,%a1
@@ -3456,7 +3596,7 @@
 	eorb	#1,%d4
 	lsll	#2,%d4		/* ! */
 	andb	#1,%d2
-	jbne	white_4
+	jbne	L(white_4)
 	bsetb	%d4,%a1@
 	addq	#1,%d4
 	bsetb	%d4,%a1@
@@ -3464,8 +3604,8 @@
 	bsetb	%d4,%a1@
 	addq	#1,%d4
 	bsetb	%d4,%a1@
-	jbra	console_plot_pixel_exit
-white_4:
+	jbra	L(console_plot_pixel_exit)
+L(white_4):
 	bclrb	%d4,%a1@
 	addq	#1,%d4
 	bclrb	%d4,%a1@
@@ -3473,38 +3613,37 @@
 	bclrb	%d4,%a1@
 	addq	#1,%d4
 	bclrb	%d4,%a1@
-	jbra	console_plot_pixel_exit
+	jbra	L(console_plot_pixel_exit)
 
-test_8bit:
+L(test_8bit):
 	cmpb	#8,%d3
-	jbne	test_16bit
+	jbne	L(test_16bit)
 	addal	%d0,%a1
 	addal	%d1,%a1
 	andb	#1,%d2
-	jbne	white_8
+	jbne	L(white_8)
 	moveb	#0xff,%a1@
-	jbra	console_plot_pixel_exit
-white_8:
+	jbra	L(console_plot_pixel_exit)
+L(white_8):
 	clrb	%a1@
-	jbra	console_plot_pixel_exit
+	jbra	L(console_plot_pixel_exit)
 
-test_16bit:
+L(test_16bit):
 	cmpb	#16,%d3
-	jbne	console_plot_pixel_exit
+	jbne	L(console_plot_pixel_exit)
 	addal	%d0,%a1
 	addal	%d0,%a1
 	addal	%d1,%a1
 	andb	#1,%d2
-	jbne	white_16
+	jbne	L(white_16)
 	clrw	%a1@
-	jbra	console_plot_pixel_exit
-white_16:
+	jbra	L(console_plot_pixel_exit)
+L(white_16):
 	movew	#0x0fff,%a1@
-	jbra	console_plot_pixel_exit
+	jbra	L(console_plot_pixel_exit)
 
-console_plot_pixel_exit:
-	moveml	%sp@+,%a0-%a1/%d0-%d4
-	rts
+L(console_plot_pixel_exit):
+func_return	console_plot_pixel
 #endif /* CONSOLE */
 
 #if 0
@@ -3549,11 +3688,6 @@
 	.long 0
 #endif
 
-#ifdef CONFIG_MAC
-L(console_video_virtual):
-	.long	0
-#endif	/* CONFIG_MAC */
-
 #if defined(CONSOLE)
 L(console_globals):
 	.long	0		/* cursor column */
@@ -3597,6 +3731,17 @@
 M147_SCC_DATA_A = 0xfffe3003
 #endif
 
+#if defined (CONFIG_MVME16x)
+M162_SCC_CTRL_A = 0xfff45005
+M167_CYCAR = 0xfff450ee
+M167_CYIER = 0xfff45011
+M167_CYLICR = 0xfff45026
+M167_CYTEOIR = 0xfff45085
+M167_CYTDR = 0xfff450f8
+M167_PCSCCTICR = 0xfff4201e
+M167_PCTPIACKR = 0xfff42025
+#endif
+
 #if defined (CONFIG_BVME6000)
 BVME_SCC_CTRL_A	= 0xffb0000b
 BVME_SCC_DATA_A	= 0xffb0000f
@@ -3630,8 +3775,8 @@
 SYMBOL_NAME_LABEL(m68k_supervisor_cachemode)
 	.long	0
 #if defined(CONFIG_MVME16x)
-SYMBOL_NAME_LABEL(mvme_bdid_ptr)
-	.long	0
+SYMBOL_NAME_LABEL(mvme_bdid)
+	.long	0,0,0,0,0,0,0,0
 #endif
 #if defined(CONFIG_Q40)
 SYMBOL_NAME_LABEL(q40_mem_cptr)
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/kgdb.c linux/arch/m68k/kernel/kgdb.c
--- linux-2.2.18-pre15/arch/m68k/kernel/kgdb.c	Thu Jul 30 11:08:19 1998
+++ linux/arch/m68k/kernel/kgdb.c	Mon Dec  3 20:33:54 2001
@@ -189,6 +189,11 @@
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
 #endif
+#ifdef CONFIG_MAC
+#include <linux/tty.h>
+#include <asm/bootinfo.h>
+#include <asm/macints.h>
+#endif
 
 
 #undef DEBUG
@@ -239,6 +244,15 @@
 extern int amiga_ser_out( unsigned char c );
 extern unsigned char amiga_ser_in( void );
 #endif
+#ifdef CONFIG_MAC
+static unsigned char mac_scca_in( void );
+static unsigned char mac_scca_out( unsigned char c );
+static unsigned char mac_scca_intr( void ); 
+static unsigned char mac_sccb_in( void );
+static unsigned char mac_sccb_out( unsigned char c);
+static unsigned char mac_sccb_intr( void );
+extern void mac_init_scc_port( int cflag, int port );
+#endif
 
 /************************* End of Prototypes **************************/
 
@@ -668,6 +682,31 @@
 	}
 #endif
 
+#ifdef CONFIG_MAC
+	if (MACH_IS_MAC) {
+		if (!strcmp( m68k_debug_device, "ser" ) ||
+		    !strcmp( m68k_debug_device, "ser1" )) {
+			mac_init_scc_port( B9600|CS8, 0 );
+			serial_in = mac_scca_in;
+			serial_out = mac_scca_out;
+			serial_intr = mac_scca_intr;
+		} else if (!strcmp( m68k_debug_device, "ser2" )) {
+			mac_init_scc_port( B9600|CS8, 1 );
+			serial_in = mac_sccb_in;
+			serial_out = mac_sccb_out;
+			serial_intr = mac_sccb_intr;
+		}
+	}
+	if (!serial_in || !serial_out) {
+		if (*m68k_debug_device)
+			printk( "kgdb_init failed: no valid serial device!\n" );
+		else
+			printk( "kgdb not enabled\n" );
+		return;
+	}
+	request_irq(4, kgdb_intr, IRQ_TYPE_FAST, "kgdb", NULL);
+#endif
+
 #ifdef CONFIG_ATARI
 	if (!serial_in || !serial_out) {
 		if (*m68k_debug_device)
@@ -781,8 +820,15 @@
   /* copy format/vector word */
   "		movew	%a0@("FRAMEOFF_VECTOR"),%a1@("GDBOFF_VECTOR")\n"
   /* save FPU regs */
+#ifndef CONFIG_FPU_EMU_ONLY
+#ifdef CONFIG_FPU_EMU
+  "		tstl	"SYMBOL_NAME_STR(m68k_fputype)"\n"
+  "		jeq	1f\n"
+#endif
   "		fmovemx	%fp0-%fp7,%a1@("GDBOFF_FP0")\n"
   "		fmoveml	%fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n"
+  "1:\n"
+#endif	/* CONFIG_FPU_EMU_ONLY */
 
   /* set stack to CPU frame */
   "		addl	#"FRAMEOFF_SR",%a0\n"
@@ -801,8 +847,15 @@
 
   /* after return, first restore FPU registers */
   "		movel	#"SYMBOL_NAME_STR(kgdb_registers)",%a0\n" /* source */
+#ifndef CONFIG_FPU_EMU_ONLY
+#ifdef CONFIG_FPU_EMU
+  "		tstl	"SYMBOL_NAME_STR(m68k_fputype)"\n"
+  "		jeq	1f\n"
+#endif
   "		fmovemx	%a0@("GDBOFF_FP0"),%fp0-%fp7\n"
   "		fmoveml	%a0@("GDBOFF_FPCTL"),%fpcr/%fpsr/%fpiar\n"
+  "1:\n"
+#endif	/* CONFIG_FPU_EMU_ONLY */
   /* set new stack pointer */
   "		movel	%a0@("GDBOFF_A7"),%sp\n"
   "		clrw	%sp@-\n"		/* fake format $0 frame */
@@ -849,8 +902,15 @@
   /* fake format 0 and vector 1 (translated to SIGINT) */
   "		movew	#4,%a1@("GDBOFF_VECTOR")\n"
   /* save FPU regs */
+#ifndef CONFIG_FPU_EMU_ONLY
+#ifdef CONFIG_FPU_EMU
+  "		tstl	"SYMBOL_NAME_STR(m68k_fputype)"\n"
+  "		jeq	1f\n"
+#endif
   "		fmovemx	%fp0-%fp7,%a1@("GDBOFF_FP0")\n"
   "		fmoveml	%fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n"
+  "1:\n"
+#endif	/* CONFIG_FPU_EMU_ONLY */
   /* pop off the CPU stack frame */
   "		addql	#8,%sp\n"
   "		movel	%sp,%a1@("GDBOFF_A7")\n" /* save a7 now */
@@ -1188,6 +1248,114 @@
 	}
 	scc.cha_b_ctrl = 0x38; /* reset highest IUS */
 	MFPDELAY();
+	return( c );
+}
+
+#endif
+
+/* -------------------- Macintosh serial I/O -------------------- */
+
+#ifdef CONFIG_MAC
+
+struct SCC
+ {
+  u_char cha_b_ctrl;
+  u_char char_dummy1;
+  u_char cha_a_ctrl;
+  u_char char_dummy2;
+  u_char cha_b_data;
+  u_char char_dummy3;
+  u_char cha_a_data;
+ };
+
+#define scc (*((volatile struct SCC*)mac_bi_data.sccbase))
+
+#define uSEC 1
+#define LONG_DELAY()				\
+    do {					\
+	int i;					\
+	for( i = 60*uSEC; i > 0; --i )		\
+	    barrier();				\
+    } while(0)
+
+static unsigned char mac_sccb_out (unsigned char c)
+{
+    int i;
+    do {
+	LONG_DELAY();
+    } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */
+    for( i = uSEC; i > 0; --i )
+	barrier();
+    scc.cha_b_data = c;
+}
+
+static unsigned char mac_scca_out (unsigned char c)
+{
+    int i;
+    do {
+	LONG_DELAY();
+    } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */
+    for( i = uSEC; i > 0; --i )
+	barrier();
+    scc.cha_a_data = c;
+}
+
+static unsigned char mac_sccb_in( void )
+{
+    do {
+	LONG_DELAY();
+    } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */
+    LONG_DELAY();
+    return( scc.cha_b_data );
+}
+
+static unsigned char mac_scca_in( void )
+
+{
+    do {
+	LONG_DELAY();
+    } while( !(scc.cha_a_ctrl & 0x01) ); /* wait for rx buf filled */
+    LONG_DELAY();
+    return( scc.cha_a_data );
+}
+
+static unsigned char mac_sccb_intr( void )
+
+{	unsigned char c, stat;
+	
+	LONG_DELAY();
+	scc.cha_b_ctrl = 1; /* RR1 */
+	LONG_DELAY();
+	stat = scc.cha_b_ctrl;
+	LONG_DELAY();
+	c = scc.cha_b_data;
+	LONG_DELAY();
+	if (stat & 0x30) {
+		scc.cha_b_ctrl = 0x30; /* error reset for overrun and parity */
+		LONG_DELAY();
+	}
+	scc.cha_b_ctrl = 0x38; /* reset highest IUS */
+	LONG_DELAY();
+	return( c );
+}
+
+static unsigned char mac_scca_intr( void )
+
+{	unsigned char c, stat;
+	
+	LONG_DELAY();
+	scc.cha_a_ctrl = 1; /* RR1 */
+	LONG_DELAY();
+	stat = scc.cha_a_ctrl;
+	LONG_DELAY();
+	c = scc.cha_a_data;
+	LONG_DELAY();
+	if (stat & 0x30) {
+		scc.cha_a_ctrl = 0x30; /* error reset for overrun and parity */
+		LONG_DELAY();
+	}
+	scc.cha_a_ctrl = 0x38; /* reset highest IUS */
+	LONG_DELAY();
 	return( c );
 }
 
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/m68k_defs.c linux/arch/m68k/kernel/m68k_defs.c
--- linux-2.2.18-pre15/arch/m68k/kernel/m68k_defs.c	Tue Jan 19 10:58:26 1999
+++ linux/arch/m68k/kernel/m68k_defs.c	Mon Dec  3 20:33:54 2001
@@ -43,8 +43,16 @@
 	/* offsets into the pt_regs */
 	DEFINE(PT_D0, offsetof(struct pt_regs, d0));
 	DEFINE(PT_ORIG_D0, offsetof(struct pt_regs, orig_d0));
+	DEFINE(PT_D1, offsetof(struct pt_regs, d1));
+	DEFINE(PT_D2, offsetof(struct pt_regs, d2));
+	DEFINE(PT_D3, offsetof(struct pt_regs, d3));
+	DEFINE(PT_D4, offsetof(struct pt_regs, d4));
+	DEFINE(PT_D5, offsetof(struct pt_regs, d5));
+	DEFINE(PT_A0, offsetof(struct pt_regs, a0));
+	DEFINE(PT_A1, offsetof(struct pt_regs, a1));
+	DEFINE(PT_A2, offsetof(struct pt_regs, a2));
+	DEFINE(PT_PC, offsetof(struct pt_regs, pc));
 	DEFINE(PT_SR, offsetof(struct pt_regs, sr));
-
 	/* bitfields are a bit difficult */
 	DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4);
 
@@ -68,6 +76,12 @@
 	DEFINE(FBCON_FONT_DESC_HEIGHT, offsetof(struct fbcon_font_desc, height));
 	DEFINE(FBCON_FONT_DESC_DATA, offsetof(struct fbcon_font_desc, data));
 	DEFINE(FBCON_FONT_DESC_PREF, offsetof(struct fbcon_font_desc, pref));
+
+	/* signal defines */
+	DEFINE(SIGSEGV, SIGSEGV);
+	DEFINE(SEGV_MAPERR, SEGV_MAPERR);
+	DEFINE(SIGTRAP, SIGTRAP);
+	DEFINE(TRAP_TRACE, TRAP_TRACE);
 
 	/* offsets into the custom struct */
 	DEFINE(CUSTOMBASE, &custom);
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/m68k_defs.h linux/arch/m68k/kernel/m68k_defs.h
--- linux-2.2.18-pre15/arch/m68k/kernel/m68k_defs.h	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/m68k_defs.h	Mon Dec  3 20:33:54 2001
@@ -8,7 +8,7 @@
 #define TASK_SIGPENDING 8
 #define TASK_NEEDRESCHED 20
 #define TASK_TSS 470
-#define TASK_MM 622
+#define TASK_MM 826
 #define TSS_KSP 0
 #define TSS_USP 4
 #define TSS_SR 8
@@ -20,6 +20,15 @@
 #define TSS_FPSTATE 132
 #define PT_D0 32
 #define PT_ORIG_D0 36
+#define PT_D1 0
+#define PT_D2 4
+#define PT_D3 8
+#define PT_D4 12
+#define PT_D5 16
+#define PT_A0 20
+#define PT_A1 24
+#define PT_A2 28
+#define PT_PC 46
 #define PT_SR 44
 #define PT_VECTOR 50
 #define IRQ_HANDLER 0
@@ -35,6 +44,10 @@
 #define FBCON_FONT_DESC_HEIGHT 12
 #define FBCON_FONT_DESC_DATA 16
 #define FBCON_FONT_DESC_PREF 20
+#define SIGSEGV 11
+#define SEGV_MAPERR 1
+#define SIGTRAP 5
+#define TRAP_TRACE 2
 #define CUSTOMBASE -2132807680
 #define C_INTENAR 28
 #define C_INTREQR 30
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- linux-2.2.18-pre15/arch/m68k/kernel/m68k_ksyms.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Mon Dec  3 20:33:54 2001
@@ -19,9 +19,13 @@
 #include <asm/hardirq.h>
 #include <asm/softirq.h>
 #include <asm/m68kserial.h>
+#include <asm/hwtest.h>
 
 asmlinkage long long __ashrdi3 (long long, int);
+asmlinkage long long __ashldi3 (long long, int);
+asmlinkage long long __lshrdi3 (long long, int);
 extern char m68k_debug_device[];
+extern void ret_from_exception(void);
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
@@ -44,6 +48,7 @@
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
 EXPORT_SYMBOL(m68k_debug_device);
+EXPORT_SYMBOL(hwreg_present);
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(strnlen);
@@ -57,6 +62,10 @@
 EXPORT_SYMBOL(kernel_thread);
 EXPORT_SYMBOL(register_serial);
 EXPORT_SYMBOL(unregister_serial);
+EXPORT_SYMBOL(ret_from_exception);
+#ifdef CONFIG_VME
+EXPORT_SYMBOL(vme_brdtype);
+#endif
 
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy);
@@ -66,6 +75,8 @@
    their interface isn't gonna change any time soon now, so
    it's OK to leave it out of version control.  */
 EXPORT_SYMBOL_NOVERS(__ashrdi3);
+EXPORT_SYMBOL_NOVERS(__ashldi3);
+EXPORT_SYMBOL_NOVERS(__lshrdi3);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memcmp);
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c
--- linux-2.2.18-pre15/arch/m68k/kernel/process.c	Wed Aug 25 17:29:46 1999
+++ linux/arch/m68k/kernel/process.c	Mon Dec  3 20:33:54 2001
@@ -61,6 +61,7 @@
 	/* endless idle loop with no priority at all */
 	current->priority = 0;
 	current->counter = -100;
+	init_idle();
 	for (;;) {
 		if (!current->need_resched)
 #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC)
@@ -78,14 +79,21 @@
 {
 	if (mach_reset)
 		mach_reset();
+	for (;;);
 }
 
 void machine_halt(void)
 {
+	if (mach_halt)
+		mach_halt();
+	for (;;);
 }
 
 void machine_power_off(void)
 {
+	if (mach_power_off)
+		mach_power_off();
+	for (;;);
 }
 
 void show_regs(struct pt_regs * regs)
@@ -146,9 +154,10 @@
 	unsigned long zero = 0;
 	set_fs(USER_DS);
 	current->tss.fs = __USER_DS;
-	asm volatile (".chip 68k/68881\n\t"
-		      "frestore %0@\n\t"
-		      ".chip 68k" : : "a" (&zero));
+	if (!FPU_IS_EMU)
+		asm volatile (".chip 68k/68881\n\t"
+			      "frestore %0\n\t"
+			      ".chip 68k" : : "m" (zero));
 }
 
 /*
@@ -209,16 +218,18 @@
 	 */
 	p->tss.fs = get_fs().seg;
 
-	/* Copy the current fpu state */
-	asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory");
-
-	if (!CPU_IS_060 ? p->tss.fpstate[0] : p->tss.fpstate[2])
-	  asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
-			"fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
-			: : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0])
-			: "memory");
-	/* Restore the state in case the fpu was busy */
-	asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0]));
+	if (!FPU_IS_EMU) {
+		/* Copy the current fpu state */
+		asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory");
+
+		if (!CPU_IS_060 ? p->tss.fpstate[0] : p->tss.fpstate[2])
+		  asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
+				"fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
+				: : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0])
+				: "memory");
+		/* Restore the state in case the fpu was busy */
+		asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0]));
+	}
 
 	return 0;
 }
@@ -227,20 +238,34 @@
 
 int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu)
 {
-  char fpustate[216];
+	char fpustate[216];
+
+	if (FPU_IS_EMU) {
+		int i;
+
+		memcpy(fpu->fpcntl, current->tss.fpcntl, 12);
+		memcpy(fpu->fpregs, current->tss.fp, 96);
+		/* Convert internal fpu reg representation
+		 * into long double format
+		 */
+		for (i = 0; i < 24; i += 3)
+			fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) |
+			                 ((fpu->fpregs[i] & 0x0000ffff) << 16);
+		return 1;
+	}
 
-  /* First dump the fpu context to avoid protocol violation.  */
-  asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
-  if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2])
-     return 0;
+	/* First dump the fpu context to avoid protocol violation.  */
+	asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
+	if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2])
+		return 0;
 
-  asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
+	asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
 		:: "m" (fpu->fpcntl[0])
 		: "memory");
-  asm volatile ("fmovemx %/fp0-%/fp7,%0"
+	asm volatile ("fmovemx %/fp0-%/fp7,%0"
 		:: "m" (fpu->fpregs[0])
 		: "memory");
-  return 1;
+	return 1;
 }
 
 /*
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/ptrace.c linux/arch/m68k/kernel/ptrace.c
--- linux-2.2.18-pre15/arch/m68k/kernel/ptrace.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/ptrace.c	Mon Dec  3 20:33:54 2001
@@ -18,6 +18,7 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/config.h>
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -403,10 +404,17 @@
 				tmp = get_reg(child, addr);
 				if (addr == PT_SR)
 					tmp >>= 16;
-			}
-			else if (addr >= 21 && addr < 49)
+			} else if (addr >= 21 && addr < 49) {
 				tmp = child->tss.fp[addr - 21];
-			else
+#ifdef CONFIG_FPU_EMU
+				/* Convert internal fpu reg representation
+				 * into long double format
+				 */
+				if (FPU_IS_EMU && (addr < 45) && !(addr % 3))
+					tmp = ((tmp & 0xffff0000) << 15) |
+					      ((tmp & 0x0000ffff) << 16);
+#endif
+			} else
 				goto out;
 			ret = put_user(tmp,(unsigned long *) data);
 			goto out;
@@ -442,6 +450,16 @@
 			}
 			if (addr >= 21 && addr < 48)
 			{
+#ifdef CONFIG_FPU_EMU
+				/* Convert long double format
+				 * into internal fpu reg representation
+				 */
+				if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) {
+					data = (unsigned long)data << 15;
+					data = (data & 0xffff0000) |
+					       ((data & 0x0000ffff) >> 1);
+				}
+#endif
 				child->tss.fp[addr - 21] = data;
 				ret = 0;
 			}
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/setup.c linux/arch/m68k/kernel/setup.c
--- linux-2.2.18-pre15/arch/m68k/kernel/setup.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/setup.c	Mon Dec  3 20:33:54 2001
@@ -41,6 +41,9 @@
 unsigned long m68k_cputype;
 unsigned long m68k_fputype;
 unsigned long m68k_mmutype;
+#ifdef CONFIG_VME
+unsigned long vme_brdtype;
+#endif
 
 int m68k_is040or060 = 0;
 
@@ -76,6 +79,8 @@
 int (*mach_hwclk) (int, struct hwclk_time*) = NULL;
 int (*mach_set_clock_mmss) (unsigned long) = NULL;
 void (*mach_reset)( void );
+void (*mach_halt)( void ) = NULL;
+void (*mach_power_off)( void ) = NULL;
 long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */
 #if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD)
 void (*mach_floppy_setup) (char *, int *) __initdata = NULL;
@@ -114,6 +119,9 @@
 extern int atari_parse_bootinfo(const struct bi_record *);
 extern int mac_parse_bootinfo(const struct bi_record *);
 extern int q40_parse_bootinfo(const struct bi_record *);
+extern int bvme6000_parse_bootinfo(const struct bi_record *);
+extern int mvme16x_parse_bootinfo(const struct bi_record *);
+extern int mvme147_parse_bootinfo(const struct bi_record *);
 
 extern void config_amiga(void);
 extern void config_atari(void);
@@ -171,6 +179,12 @@
 		    unknown = mac_parse_bootinfo(record);
 		else if (MACH_IS_Q40)
 		    unknown = q40_parse_bootinfo(record);
+		else if (MACH_IS_BVME6000)
+		    unknown = bvme6000_parse_bootinfo(record);
+		else if (MACH_IS_MVME16x)
+		    unknown = mvme16x_parse_bootinfo(record);
+		else if (MACH_IS_MVME147)
+		    unknown = mvme147_parse_bootinfo(record);
 		else
 		    unknown = 1;
 	}
@@ -208,10 +222,12 @@
 	base_trap_init();
 
 	/* clear the fpu if we have one */
+#ifndef CONFIG_FPU_EMU_ONLY
 	if (m68k_fputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) {
 		volatile int zero = 0;
 		asm __volatile__ ("frestore %0" : : "m" (zero));
 	}
+#endif
 
 	init_task.mm->start_code = PAGE_OFFSET;
 	init_task.mm->end_code = (unsigned long) &_etext;
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/signal.c linux/arch/m68k/kernel/signal.c
--- linux-2.2.18-pre15/arch/m68k/kernel/signal.c	Thu Jan  7 08:41:55 1999
+++ linux/arch/m68k/kernel/signal.c	Mon Dec  3 20:33:54 2001
@@ -14,6 +14,10 @@
  * 68060 fixes by Jesper Skov
  *
  * 1997-12-01  Modified for POSIX.1b signals by Andreas Schwab
+ *
+ * mathemu support by Roman Zippel
+ *  (Note: fpstate in the signal context is completly ignored for the emulator
+ *         and the internal floating point format is put on stack)
  */
 
 /*
@@ -156,6 +160,9 @@
 
 /*
  * Do a signal return; undo the signal stack.
+ *
+ * Keep the return code on the stack quadword aligned!
+ * That makes the cache flush below easier.
  */
 
 struct sigframe
@@ -175,9 +182,9 @@
 	int sig;
 	struct siginfo *pinfo;
 	void *puc;
+	char retcode[8];
 	struct siginfo info;
 	struct ucontext uc;
-	char retcode[8];
 };
 
 
@@ -187,6 +194,13 @@
 {
 	int err = 1;
 
+	if (FPU_IS_EMU) {
+	    /* restore registers */
+	    memcpy(current->tss.fpcntl, sc->sc_fpcntl, 12);
+	    memcpy(current->tss.fp, sc->sc_fpregs, 24);
+	    return 0;
+	}
+
 	if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {
 	    /* Verify the frame format.  */
 	    if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version))
@@ -239,6 +253,18 @@
 	fpregset_t fpregs;
 	int err = 1;
 
+	if (FPU_IS_EMU) {
+		/* restore fpu control register */
+		if (__copy_from_user(current->tss.fpcntl,
+				&uc->uc_mcontext.fpregs.f_pcr, 12))
+			goto out;
+		/* restore all other fpu register */
+		if (__copy_from_user(current->tss.fp,
+				uc->uc_mcontext.fpregs.f_fpregs, 96))
+			goto out;
+		return 0;
+	}
+
 	if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate))
 		goto out;
 	if (CPU_IS_060 ? fpstate[2] : fpstate[0]) {
@@ -478,7 +504,7 @@
 	struct switch_stack *sw = (struct switch_stack *) &__unused;
 	struct pt_regs *regs = (struct pt_regs *) (sw + 1);
 	unsigned long usp = rdusp();
-	struct sigframe *frame = (struct sigframe *)(usp - 24);
+	struct sigframe *frame = (struct sigframe *)(usp - 4);
 	sigset_t set;
 	int d0;
 
@@ -536,6 +562,13 @@
 
 static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)
 {
+	if (FPU_IS_EMU) {
+		/* save registers */
+		memcpy(sc->sc_fpcntl, current->tss.fpcntl, 12);
+		memcpy(sc->sc_fpregs, current->tss.fp, 24);
+		return;
+	}
+
 	__asm__ volatile (".chip 68k/68881\n\t"
 			  "fsave %0\n\t"
 			  ".chip 68k"
@@ -567,6 +600,16 @@
 	int context_size = CPU_IS_060 ? 8 : 0;
 	int err = 0;
 
+	if (FPU_IS_EMU) {
+		/* save fpu control register */
+		err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr,
+				current->tss.fpcntl, 12);
+		/* save all other fpu register */
+		err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs,
+				current->tss.fp, 96);
+		return err;
+	}
+
 	__asm__ volatile (".chip 68k/68881\n\t"
 			  "fsave %0\n\t"
 			  ".chip 68k"
@@ -677,25 +720,6 @@
 				      "cpushl %%bc,(%0)\n\t"
 				      ".chip 68k"
 				      : : "a" (temp));
-		if (((vaddr + 8) ^ vaddr) & ~15) {
-			if (((vaddr + 8) ^ vaddr) & PAGE_MASK)
-				__asm__ __volatile__ (".chip 68040\n\t"
-						      "nop\n\t"
-						      "ptestr (%1)\n\t"
-						      "movec %%mmusr,%0\n\t"
-						      ".chip 68k"
-						      : "=r" (temp)
-						      : "a" (vaddr + 8));
-
-			temp &= PAGE_MASK;
-			temp |= (vaddr + 8) & ~PAGE_MASK;
-
-			__asm__ __volatile__ (".chip 68040\n\t"
-					      "nop\n\t"
-					      "cpushl %%bc,(%0)\n\t"
-					      ".chip 68k"
-					      : : "a" (temp));
-		}
 	}
 	else if (CPU_IS_060) {
 		unsigned long temp;
@@ -708,18 +732,6 @@
 				      "cpushl %%bc,(%0)\n\t"
 				      ".chip 68k"
 				      : : "a" (temp));
-		if (((vaddr + 8) ^ vaddr) & ~15) {
-			if (((vaddr + 8) ^ vaddr) & PAGE_MASK)
-				__asm__ __volatile__ (".chip 68060\n\t"
-						      "plpar (%0)\n\t"
-						      ".chip 68k"
-						      : "=a" (temp)
-						      : "0" (vaddr + 8));
-			__asm__ __volatile__ (".chip 68060\n\t"
-					      "cpushl %%bc,(%0)\n\t"
-					      ".chip 68k"
-					      : : "a" (temp));
-		}
 	}
 	else {
 		/*
@@ -797,11 +809,9 @@
 
 	/* Set up to return from userspace.  */
 	err |= __put_user(frame->retcode, &frame->pretcode);
-	/* addaw #20,sp */
-	err |= __put_user(0xdefc0014, (long *)(frame->retcode + 0));
 	/* moveq #,d0; trap #0 */
 	err |= __put_user(0x70004e40 + (__NR_sigreturn << 16),
-			  (long *)(frame->retcode + 4));
+			  (long *)(frame->retcode));
 
 	if (err)
 		goto give_sigsegv;
@@ -881,10 +891,10 @@
 
 	/* Set up to return from userspace.  */
 	err |= __put_user(frame->retcode, &frame->pretcode);
-	/* movel #,d0; trap #0 */
-	err |= __put_user(0x203c, (short *)(frame->retcode + 0));
-	err |= __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2));
-	err |= __put_user(0x4e40, (short *)(frame->retcode + 6));
+	/* moveq #,d0; notb d0; trap #0 */
+	err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16),
+			  (long *)(frame->retcode + 0));
+	err |= __put_user(0x4e40, (short *)(frame->retcode + 4));
 
 	if (err)
 		goto give_sigsegv;
@@ -964,11 +974,10 @@
 	if (ka->sa.sa_flags & SA_ONESHOT)
 		ka->sa.sa_handler = SIG_DFL;
 
-	if (!(ka->sa.sa_flags & SA_NODEFER)) {
-		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+	sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+	if (!(ka->sa.sa_flags & SA_NODEFER))
 		sigaddset(&current->blocked,sig);
-		recalc_sigpending(current);
-	}
+	recalc_sigpending(current);
 }
 
 /*
@@ -1092,6 +1101,7 @@
 
 			default:
 				sigaddset(&current->signal, signr);
+				recalc_sigpending(current);
 				current->flags |= PF_SIGNALED;
 				do_exit(exit_code);
 				/* NOTREACHED */
diff -urN linux-2.2.18-pre15/arch/m68k/kernel/traps.c linux/arch/m68k/kernel/traps.c
--- linux-2.2.18-pre15/arch/m68k/kernel/traps.c	Mon Oct  5 13:54:39 1998
+++ linux/arch/m68k/kernel/traps.c	Mon Dec  3 20:33:54 2001
@@ -41,12 +41,17 @@
 #include <asm/kgdb.h>
 #endif
 
+/*#define DEBUG*/
+
 /* assembler routines */
 asmlinkage void system_call(void);
 asmlinkage void buserr(void);
 asmlinkage void trap(void);
 asmlinkage void inthandler(void);
 asmlinkage void nmihandler(void);
+#ifdef CONFIG_FPU_EMU
+asmlinkage void fpu_emu(void);
+#endif
 
 e_vector vectors[256] = {
 	0, 0, buserr, trap, trap, trap, trap, trap,
@@ -70,7 +75,7 @@
 	/* setup the exception vector table */
 	__asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors));
 
-	if (CPU_IS_040) {
+	if (CPU_IS_040 && !FPU_IS_EMU) {
 		/* set up FPSP entry points */
 		asmlinkage void dz_vec(void) asm ("dz");
 		asmlinkage void inex_vec(void) asm ("inex");
@@ -93,6 +98,12 @@
 		vectors[VEC_FPUNSUP] = unsupp_vec;
 	}
 	if (CPU_IS_060) {
+		/* set up ISP entry points */
+		asmlinkage void unimp_vec(void) asm ("_060_isp_unimp");
+
+		vectors[VEC_UNIMPII] = unimp_vec;
+	}
+	if (CPU_IS_060 && !FPU_IS_EMU) {
 		/* set up IFPSP entry points */
 		asmlinkage void snan_vec(void) asm ("_060_fpsp_snan");
 		asmlinkage void operr_vec(void) asm ("_060_fpsp_operr");
@@ -104,8 +115,6 @@
 		asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp");
 		asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd");
 
-		asmlinkage void unimp_vec(void) asm ("_060_isp_unimp");
-
 		vectors[VEC_FPNAN] = snan_vec;
 		vectors[VEC_FPOE] = operr_vec;
 		vectors[VEC_FPOVER] = ovfl_vec;
@@ -115,10 +124,6 @@
 		vectors[VEC_LINE11] = fline_vec;
 		vectors[VEC_FPUNSUP] = unsupp_vec;
 		vectors[VEC_UNIMPEA] = effadd_vec;
-
-		/* set up ISP entry points */
-
-		vectors[VEC_UNIMPII] = unimp_vec;
 	}
 }
 
@@ -133,6 +138,11 @@
 	for (i = 64; i < 256; i++)
 		vectors[i] = inthandler;
 
+#ifdef CONFIG_FPU_EMU
+	if (FPU_IS_EMU)
+		vectors[VEC_LINE11] = fpu_emu;
+#endif
+
         /* if running on an amiga, make the NMI interrupt do nothing */
 	if (MACH_IS_AMIGA) {
 		vectors[VEC_INT7] = nmihandler;
@@ -182,8 +192,9 @@
 
 
 void die_if_kernel(char *,struct pt_regs *,int);
-asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
-                             unsigned long error_code);
+int do_page_fault(struct pt_regs *regs, unsigned long address,
+                  unsigned long error_code);
+int send_fault_sig(struct pt_regs *regs);
 
 asmlinkage void trap_c(struct frame *fp);
 
@@ -232,74 +243,141 @@
 #endif /* CONFIG_M68060 */
 
 #if defined (CONFIG_M68040)
-static inline unsigned long probe040 (int iswrite, int fc, unsigned long addr)
+
+static inline unsigned long probe040(int iswrite, unsigned long addr)
 {
 	unsigned long mmusr;
-	mm_segment_t fs = get_fs();
 
-	set_fs (MAKE_MM_SEG(fc));
+	asm volatile (".chip 68040");
 
 	if (iswrite)
-		/* write */
-		asm volatile (".chip 68040\n\t"
-			      "ptestw (%1)\n\t"
-			      "movec %%mmusr,%0\n\t"
-			      ".chip 68k"
-			      : "=r" (mmusr)
-			      : "a" (addr));
+		asm volatile ("ptestw (%0)" : : "a" (addr));
 	else
-		asm volatile (".chip 68040\n\t"
-			      "ptestr (%1)\n\t"
-			      "movec %%mmusr,%0\n\t"
-			      ".chip 68k"
-			      : "=r" (mmusr)
-			      : "a" (addr));
+		asm volatile ("ptestr (%0)" : : "a" (addr));
+
+	asm volatile ("movec %%mmusr,%0" : "=r" (mmusr));
 
-	set_fs (fs);
+	asm volatile (".chip 68k");
 
 	return mmusr;
 }
 
-static inline void do_040writeback (unsigned short ssw,
-			     unsigned short wbs,
-			     unsigned long wba,
-			     unsigned long wbd,
-			     struct frame *fp)
+static inline int do_040writeback1(unsigned short wbs, unsigned long wba,
+				   unsigned long wbd)
 {
-	mm_segment_t fs = get_fs ();
-	unsigned long mmusr;
-	unsigned long errorcode;
+	mm_segment_t old_fs;
+	int res = 0;
 
-	/*
-	 * No special handling for the second writeback anymore.
-	 * It misinterpreted the misaligned status sometimes.
-	 * This way an extra page-fault may be caused (Martin Apel).
-	 */
-
-	mmusr = probe040 (1, wbs & WBTM_040,  wba);
-	errorcode = (mmusr & MMU_R_040) ? 3 : 2;
-	if (do_page_fault (&fp->ptregs, wba, errorcode))
-	  /* just return if we can't perform the writeback */
-	  return;
+	old_fs = get_fs();
+	set_fs(MAKE_MM_SEG(wbs));
 
-	set_fs (MAKE_MM_SEG(wbs & WBTM_040));
 	switch (wbs & WBSIZ_040) {
-	    case BA_SIZE_BYTE:
-		put_user (wbd & 0xff, (char *)wba);
+	case BA_SIZE_BYTE:
+		res = put_user(wbd & 0xff, (char *)wba);
 		break;
-	    case BA_SIZE_WORD:
-		put_user (wbd & 0xffff, (short *)wba);
+	case BA_SIZE_WORD:
+		res = put_user(wbd & 0xffff, (short *)wba);
 		break;
-	    case BA_SIZE_LONG:
-		put_user (wbd, (int *)wba);
+	case BA_SIZE_LONG:
+		res = put_user(wbd, (int *)wba);
 		break;
 	}
-	set_fs (fs);
+	set_fs(old_fs);
+
+#ifdef DEBUG
+	printk("do_040writeback1, res=%d\n",res);
+#endif
+
+	return res;
+}
+
+#define FIX_XFRAME(fp,_faddr_,_set_ma_,_wbs_)           \
+     do{ (fp)->un.fmt7.faddr = (_faddr_);                 \
+         (fp)->un.fmt7.ssw &= ~( 0x7f | MA_040 | RW_040); \
+         (fp)->un.fmt7.ssw |= (~0x7f & (_wbs_));          \
+         (fp)->un.fmt7.ssw |= ((_set_ma_) ? MA_040 : 0);  \
+       } while(0)
+
+/* after an exception in a writeback the stack frame coresponding
+ * to that exception is discarded, set a few bits in the old frame 
+ * to simulate what it should look like       */
+
+inline void fix_xframe040(struct frame *fp, unsigned long faddr, unsigned short wbs)
+{
+  unsigned short ssw=(fp)->un.fmt7.ssw;
+
+  ssw &= ~( 0x7f | MA_040 | RW_040);
+  ssw |= (~0x7f & wbs);
+  if (faddr != (unsigned long)(current->buserr_info.si_addr) )
+    ssw |= MA_040;
+  (fp)->un.fmt7.faddr = faddr;
+  (fp)->un.fmt7.ssw = ssw;
+}
+
+inline void do_040writebacks(struct frame *fp)
+{
+        unsigned long wbaddr;
+	int xp=0;
+#if 0
+	if (fp->un.fmt7.wb1s & WBV_040)
+		printk("access_error040: cannot handle 1st writeback. oops.\n");
+#endif
+
+	if ((fp->un.fmt7.wb2s & WBV_040) &&
+	    !(fp->un.fmt7.wb2s & WBTT_040)) {
+		wbaddr = fp->un.fmt7.wb2a;
+		if (xp=do_040writeback1(fp->un.fmt7.wb2s, wbaddr,
+				     fp->un.fmt7.wb2d))
+		  {
+		    fix_xframe040(fp,wbaddr,fp->un.fmt7.wb2s);
+		  }
+		else 
+		  fp->un.fmt7.wb2s &= ~WBV_040;
+	}
+
+	if (fp->un.fmt7.wb3s & WBV_040) {
+		wbaddr= fp->un.fmt7.wb3a;
+		if (do_040writeback1(fp->un.fmt7.wb3s, wbaddr,
+				     fp->un.fmt7.wb3d))
+		  {
+		    if (!xp)
+		      {
+			fix_xframe040(fp,wbaddr,fp->un.fmt7.wb3s);
+			fp->un.fmt7.wb2s = fp->un.fmt7.wb3s;
+			fp->un.fmt7.wb3s &= (~WBV_040);
+			fp->un.fmt7.wb2a = wbaddr;
+			fp->un.fmt7.wb2d = fp->un.fmt7.wb3d;
+			xp=1;
+		      }
+		  }
+		else 
+		  fp->un.fmt7.wb3s &= ~WBV_040;
+	}
+
+	if (!xp) return;
+
+	send_fault_sig(&fp->ptregs);
+}
+
+/*
+ * called from sigreturn(), must ensure userspace code didn't
+ * manipulate exception frame to circumvent protection, then complete
+ * pending writebacks
+ * Theory is that setting TT1=0 and TM2=0 will avoid all trouble
+ */
+asmlinkage void berr_040cleanup(struct frame *fp)
+{
+        fp->un.fmt7.ssw  &= ~(0x10 | 0x4);
+	fp->un.fmt7.wb2s &= ~(0x10 | 0x4);
+	fp->un.fmt7.wb3s &= ~(0x10 | 0x4);
+
+	do_040writebacks(fp);
 }
 
-static inline void access_error040 (struct frame *fp)
+static inline void access_error040(struct frame *fp)
 {
 	unsigned short ssw = fp->un.fmt7.ssw;
+	mm_segment_t old_fs = get_fs();
 	unsigned long mmusr;
 
 #ifdef DEBUG
@@ -323,43 +401,43 @@
 		if (ssw & MA_040)
 			addr = PAGE_ALIGN (addr);
 
+		set_fs(MAKE_MM_SEG(ssw));
 		/* MMU error, get the MMUSR info for this access */
-		mmusr = probe040 (!(ssw & RW_040), ssw & TM_040, addr);
+		mmusr = probe040(!(ssw & RW_040), addr);
+		set_fs(old_fs);
 #ifdef DEBUG
 		printk("mmusr = %lx\n", mmusr);
 #endif
+
 		errorcode = ((mmusr & MMU_R_040) ? 1 : 0) |
-			((ssw & RW_040) ? 0 : 2);
-		do_page_fault (&fp->ptregs, addr, errorcode);
-	} else {
-		printk ("68040 access error, ssw=%x\n", ssw);
-		trap_c (fp);
-	}
+			    ((ssw & RW_040) ? 0 : 2) |
+		            ((ssw & LK_040) ? 2 : 0);
 
-#if 0
-	if (fp->un.fmt7.wb1s & WBV_040)
-		printk("access_error040: cannot handle 1st writeback. oops.\n");
+		if (do_page_fault(&fp->ptregs, addr, errorcode)) {
+#ifdef DEBUG
+		        printk("do_page_fault() !=0 \n");
 #endif
+			if (user_mode(&fp->ptregs)){
+				/* delay writebacks after signal delivery */
+#ifdef DEBUG
+			        printk(".. was usermode - return\n");
+#endif
+				return;
+			}
+			/* disable writeback into user space from kernel */
+#ifdef DEBUG
+			printk(".. disabling wb2\n");
+#endif
+			if (fp->un.fmt7.wb2a == fp->un.fmt7.faddr)
+				fp->un.fmt7.wb2s &= ~WBV_040;
+		}
 
-/*
- *  We may have to do a couple of writebacks here.
- *
- *  MR: we can speed up the thing a little bit and let do_040writeback()
- *  not produce another page fault as wb2 corresponds to the address that
- *  caused the fault. on write faults no second fault is generated, but
- *  on read faults for security reasons (although per definitionem impossible)
- */
+	} else {
+		printk("68040 access error, ssw=%x\n", ssw);
+		trap_c(fp);
+	}
 
-	if (fp->un.fmt7.wb2s & WBV_040 && (fp->un.fmt7.wb2s &
-					   WBTT_040) != BA_TT_MOVE16)
-		do_040writeback (ssw,
-				 fp->un.fmt7.wb2s, fp->un.fmt7.wb2a,
-				 fp->un.fmt7.wb2d, fp);
-
-	if (fp->un.fmt7.wb3s & WBV_040)
-		do_040writeback (ssw, fp->un.fmt7.wb3s,
-				 fp->un.fmt7.wb3a, fp->un.fmt7.wb3d,
-				 fp);
+	do_040writebacks(fp);
 }
 #endif /* CONFIG_M68040 */
 
@@ -650,7 +728,7 @@
 	if (user_mode(&fp->ptregs))
 		current->tss.esp0 = (unsigned long) fp;
 
-#if DEBUG
+#if 0/*DEBUG*/
 	printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format);
 #endif
 
@@ -673,7 +751,7 @@
 #endif
 	default:
 	  die_if_kernel("bad frame format",&fp->ptregs,0);
-#if DEBUG
+#if 0/*DEBUG*/
 	  printk("Unknown SIGSEGV - 4\n");
 #endif
 	  force_sig(SIGSEGV, current);
@@ -990,3 +1068,16 @@
 {
 	do_exit(SIGSEGV);
 }
+
+#ifdef CONFIG_FPU_EMU
+asmlinkage void fpemu_signal(int signal, int code, void *addr)
+{
+	siginfo_t info;
+
+	info.si_signo = signal;
+	info.si_errno = 0;
+	info.si_code = code;
+	info.si_addr = addr;
+	force_sig_info(signal, &info, current);
+}
+#endif
diff -urN linux-2.2.18-pre15/arch/m68k/lib/Makefile linux/arch/m68k/lib/Makefile
--- linux-2.2.18-pre15/arch/m68k/lib/Makefile	Wed Sep 25 00:47:40 1996
+++ linux/arch/m68k/lib/Makefile	Mon Dec  3 20:33:54 2001
@@ -6,6 +6,6 @@
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
 
 L_TARGET = lib.a
-L_OBJS  = ashrdi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o
+L_OBJS  = ashrdi3.o lshrdi3.o ashldi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre15/arch/m68k/lib/ashldi3.c linux/arch/m68k/lib/ashldi3.c
--- linux-2.2.18-pre15/arch/m68k/lib/ashldi3.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/lib/ashldi3.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,62 @@
+/* ashldi3.c extracted from gcc-2.8.1/libgcc2.c which is: */
+/* Copyright (C) 1989, 92-97, 1998 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#define BITS_PER_UNIT 8
+
+typedef          int SItype     __attribute__ ((mode (SI)));
+typedef unsigned int USItype    __attribute__ ((mode (SI)));
+typedef          int DItype     __attribute__ ((mode (DI)));
+typedef int word_type __attribute__ ((mode (__word__)));
+
+struct DIstruct {SItype high, low;};
+
+typedef union
+{
+  struct DIstruct s;
+  DItype ll;
+} DIunion;
+
+DItype
+__ashldi3 (DItype u, word_type b)
+{
+  DIunion w;
+  word_type bm;
+  DIunion uu;
+
+  if (b == 0)
+    return u;
+
+  uu.ll = u;
+
+  bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+  if (bm <= 0)
+    {
+      w.s.low = 0;
+      w.s.high = (USItype)uu.s.low << -bm;
+    }
+  else
+    {
+      USItype carries = (USItype)uu.s.low >> bm;
+      w.s.low = (USItype)uu.s.low << b;
+      w.s.high = ((USItype)uu.s.high << b) | carries;
+    }
+
+  return w.ll;
+}
diff -urN linux-2.2.18-pre15/arch/m68k/lib/lshrdi3.c linux/arch/m68k/lib/lshrdi3.c
--- linux-2.2.18-pre15/arch/m68k/lib/lshrdi3.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/lib/lshrdi3.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,62 @@
+/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#define BITS_PER_UNIT 8
+
+typedef 	 int SItype	__attribute__ ((mode (SI)));
+typedef unsigned int USItype	__attribute__ ((mode (SI)));
+typedef		 int DItype	__attribute__ ((mode (DI)));
+typedef int word_type __attribute__ ((mode (__word__)));
+
+struct DIstruct {SItype high, low;};
+
+typedef union
+{
+  struct DIstruct s;
+  DItype ll;
+} DIunion;
+
+DItype
+__lshrdi3 (DItype u, word_type b)
+{
+  DIunion w;
+  word_type bm;
+  DIunion uu;
+
+  if (b == 0)
+    return u;
+
+  uu.ll = u;
+
+  bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+  if (bm <= 0)
+    {
+      w.s.high = 0;
+      w.s.low = (USItype)uu.s.high >> -bm;
+    }
+  else
+    {
+      USItype carries = (USItype)uu.s.high << bm;
+      w.s.high = (USItype)uu.s.high >> b;
+      w.s.low = ((USItype)uu.s.low >> b) | carries;
+    }
+
+  return w.ll;
+}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/Makefile linux/arch/m68k/mac/Makefile
--- linux-2.2.18-pre15/arch/m68k/mac/Makefile	Sat Jun 13 13:14:32 1998
+++ linux/arch/m68k/mac/Makefile	Mon Dec  3 20:33:54 2001
@@ -7,11 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-EXTRA_CFLAGS := -Wa,-m68020
-
 O_TARGET := mac.o
-O_OBJS	 := config.o bootparse.o macints.o via6522.o \
-		mackeyb.o adb-bus.o macboing.o debug.o
+O_OBJS	 := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \
+		mackeyb.o adb-bus.o macboing.o debug.o adb-misc.o
 OX_OBJS  := mac_ksyms.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre15/arch/m68k/mac/adb-bus.c linux/arch/m68k/mac/adb-bus.c
--- linux-2.2.18-pre15/arch/m68k/mac/adb-bus.c	Mon Oct  5 13:54:39 1998
+++ linux/arch/m68k/mac/adb-bus.c	Mon Dec  3 20:33:54 2001
@@ -11,6 +11,28 @@
  *			- Guido Koerber's session with a logic analyzer
  *
  *	MSch (1/98) Integrated start of IIsi driver by Robert Thompson
+ *
+ *      1999-05-30 (JMT) - Added support for reading/writing the PRAM clock.
+ *			   We really need to clean up the reply_expected usage
+ *			   in here so that the special cases in adb_interrupt
+ *			   for faking replies can be removed.
+ *
+ *      1999-06-10 (JMT) - Wedged in some IOP support. This is really getting
+ *			   messy...
+ *
+ *      1999-06-11 (JMT) - IOP support sort of working. Autopolling of the
+ *			   keyboard works but if we transmit and get a timeout
+ *			   we'll get bombarded with timeout message from the
+ *			   IOP and I don't know how to make them stop. Thus
+ *			   I have disabled transmission until I can figure out
+ *			   how to handle the timeouts properly.
+ *      1999-06-12 (JMT) - Rewrote IOP support to match changes in IOP
+ *			   architecture. Also discovered that you need to send
+ *			   a sensible reply to an autopoll packet or else the
+ *			   keyboard will stop sending packets. I chose to send
+ *			   back what we received, as that was what we were
+ *			   essentially doing before the IOP rewrite and it
+ *			   seemed to work.
  */
  
 #include <stdarg.h>
@@ -22,7 +44,6 @@
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/mm.h>
-#include "via6522.h"
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -32,7 +53,9 @@
 #include <asm/setup.h>
 #include <asm/macintosh.h>
 #include <asm/macints.h>
-
+#include <asm/mac_via.h>
+#include <asm/mac_iop.h>
+#include <asm/adb_iop.h>
 
 #define MACII		/* For now - will be a switch */
 
@@ -51,11 +74,7 @@
 
 /* Bits in ACR */
 #define SR_CTRL		0x1c		/* Shift register control bits */
-#ifdef USE_ORIG
-#define SR_EXT		0x1c		/* Shift on external clock */
-#else
 #define SR_EXT		0x0c		/* Shift on external clock */
-#endif
 #define SR_OUT		0x10		/* Shift out if 1 */
 
 /* Bits in IFR and IER */
@@ -68,6 +87,17 @@
 /* JRT */
 #define ADB_DELAY 150
 
+#ifdef DEBUG_ADB_INTS
+static int nclock, ndata;
+#endif
+
+static int need_poll    = 0;
+static int command_byte = 0;
+static int last_reply   = 0;
+static int last_active  = 0;
+
+static struct adb_request *retry_req;
+
 static struct adb_handler {
     void (*handler)(unsigned char *, int, struct pt_regs *);
 } adb_handler[16];
@@ -100,14 +130,23 @@
 int in_keybinit = 1;
 
 static void adb_start(void);
-extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_pm_interrupt(int irq, void *arg, struct pt_regs *regs);
+#ifdef DEBUG_ADB_INTS
+static void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs);
+#endif
 static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static void adb_iop_receive(struct iop_msg *, struct pt_regs *);
 
+static void adb_hw_setup_macII(void);
 static void adb_hw_setup_IIsi(void);
 static void adb_hw_setup_cuda(void);
+static void adb_hw_setup_iop(void);
+static void adb_hw_setup_pm(void);
+
+void adb_retransmit(int);
 
 /*
  * debug level 10 required for ADB logging (should be && debug_adb, ideally)
@@ -149,7 +188,6 @@
 void adb_bus_init(void)
 {
 	unsigned long flags;
-	unsigned char c, i;
 	
 	save_flags(flags);
 	cli();
@@ -162,122 +200,47 @@
 	{
 	
 		case MAC_ADB_II:
-			printk("adb: MacII style keyboard/mouse driver.\n");
-			/* Set the lines up. We want TREQ as input TACK|TIP as output */
-		 	via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-			/*
-			 * Docs suggest TREQ should be output - that seems nuts
-			 * BSD agrees here :-)
-			 * Setup vPCR ??
-			 */
-
-#ifdef USE_ORIG
-		 	/* Lower the bus signals (MacII is active low it seems ???) */
-		 	via_write(via1, vBufB, via_read(via1, vBufB)&~TACK);
-#else
-			/* Corresponding state: idle (clear state bits) */
-		 	via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-			last_status = (via_read(via1, vBufB)&~ST_MASK);
-#endif
-		 	/* Shift register on input */
-		 	c=via_read(via1, vACR);
-		 	c&=~SR_CTRL;		/* Clear shift register bits */
-		 	c|=SR_EXT;		/* Shift on external clock; out or in? */
-		 	via_write(via1, vACR, c);
-		 	/* Wipe any pending data and int */
-		 	via_read(via1, vSR);
-
-		 	/* This is interrupts on enable SR for keyboard */
-		 	via_write(via1, vIER, IER_SET|SR_INT); 
-		 	/* This clears the interrupt bit */
-		 	via_write(via1, vIFR, SR_INT);
-
-		 	/*
-		 	 *	Ok we probably ;) have a ready to use adb bus. Its also
-		 	 *	hopefully idle (Im assuming the mac didnt leave a half
-		 	 *	complete transaction on booting us).
- 			 */
- 
+			printk(KERN_INFO "adb: MacII style keyboard/mouse driver.\n");
+			adb_hw_setup_macII(); 
 			request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, 
 				    "adb interrupt", adb_interrupt);
 			adb_state = idle;
 			break;
-			/*
-			 *	Unsupported; but later code doesn't notice !!
-			 */
-		case MAC_ADB_CUDA:
-			printk("adb: CUDA interface.\n");
-#if 0
-			/* don't know what to set up here ... */
-			adb_state = idle;
-			/* Set the lines up. We want TREQ as input TACK|TIP as output */
-		 	via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-#endif
-			adb_hw_setup_cuda();
-			adb_state = idle;
-			request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, 
-				    "adb CUDA interrupt", adb_cuda_interrupt);
-			break;
 		case MAC_ADB_IISI:
 			printk("adb: Using IIsi hardware.\n");
-			printk("\tDEBUG_JRT\n");
-			/* Set the lines up. We want TREQ as input TACK|TIP as output */
-		 	via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-
-			/*
-			 * MSch: I'm pretty sure the setup is mildly wrong
-			 * for the IIsi. 
-			 */
-			/* Initial state: idle (clear state bits) */
-		 	via_write(via1, vBufB, (via_read(via1, vBufB) & ~(TIP|TACK)) );
-			last_status = (via_read(via1, vBufB)&~ST_MASK);
-		 	/* Shift register on input */
-		 	c=via_read(via1, vACR);
-		 	c&=~SR_CTRL;		/* Clear shift register bits */
-		 	c|=SR_EXT;		/* Shift on external clock; out or in? */
-		 	via_write(via1, vACR, c);
-		 	/* Wipe any pending data and int */
-		 	via_read(via1, vSR);
-
-		 	/* This is interrupts on enable SR for keyboard */
-		 	via_write(via1, vIER, IER_SET|SR_INT); 
-		 	/* This clears the interrupt bit */
-		 	via_write(via1, vIFR, SR_INT);
-
-			/* get those pesky clock ticks we missed while booting */
-			for ( i = 0; i < 60; i++) {
-				udelay(ADB_DELAY);
-				adb_hw_setup_IIsi();
-				udelay(ADB_DELAY);
-				if (via_read(via1, vBufB) & TREQ)
-					break;
-			}
-			if (i == 60)
-				printk("adb_IIsi: maybe bus jammed ??\n");
-
-		 	/*
-		 	 *	Ok we probably ;) have a ready to use adb bus. Its also
- 			 */
+			adb_hw_setup_IIsi();
 			request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, 
 				    "adb interrupt", adb_cuda_interrupt);
 			adb_state = idle;
  			break;
+		case MAC_ADB_CUDA:
+			printk(KERN_INFO "adb: CUDA interface.\n");
+			
+			adb_hw_setup_cuda();
+			request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, 
+				    "adb CUDA interrupt", adb_cuda_interrupt);
+			adb_state = idle;
+			break;
+		case MAC_ADB_IOP:
+			printk("adb: using IOP for ADB...good luck.\n");
+			iop_listen(ADB_IOP, ADB_CHAN, adb_iop_receive, "ADB");
+			adb_hw_setup_iop();
+			adb_state = idle;
+			break;
+	        case MAC_ADB_PB1:
+        	case MAC_ADB_PB2:
+			printk("adb: using PowerManager for ADB... you are doomed.\n");
+			adb_hw_setup_pm();
+			request_irq(IRQ_MAC_ADB, adb_pm_interrupt, IRQ_FLG_LOCK, 
+				    "adb PowerManager interrupt", adb_pm_interrupt);
+			adb_state = idle;
+			break;
 		default:
 			printk("adb: Unknown hardware interface.\n");
-		nosupp:
-			printk("adb: Interface unsupported.\n");
 			restore_flags(flags);	
 			return;
 	}
 
-	/*
-	 * XXX: interrupt only registered if supported HW !!
-	 * -> unsupported HW will just time out on keyb_init!
-	 */
-#if 0
-	request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, 
-		    "adb interrupt", adb_interrupt);
-#endif
 #ifdef DEBUG_ADB_INTS
 	request_irq(IRQ_MAC_ADB_CL, adb_clock_interrupt, IRQ_FLG_LOCK, 
 		    "adb clock interrupt", adb_clock_interrupt);
@@ -289,6 +252,100 @@
 	restore_flags(flags);	
 } 
 	
+/* Set up IOP ADB */
+
+void adb_hw_setup_iop(void)
+{
+#if 0
+	volatile struct adb_request req;
+#endif
+
+	printk("adb-iop: hardware setup in progress.\n");
+
+#if 0
+	adb_request((struct adb_request *) &req, NULL,
+			2, ADB_PACKET, AIF_RESET);
+	while (!req.complete);
+#endif
+
+	printk("adb-iop: hardware setup done!\n");
+}
+
+/* Strip the adb message out of an IOP message */
+
+static void adb_from_iopmsg(struct iop_msg *src, struct adb_iopmsg *dst,
+				int is_reply)
+{
+	if (is_reply) {
+		memcpy(dst, &src->reply[0], sizeof(struct adb_iopmsg));
+	} else {
+		memcpy(dst, &src->message[0], sizeof(struct adb_iopmsg));
+	}
+}
+
+/*
+ * Receive an ADB message from the IOP.
+ *
+ * This will be called in two cases:
+ *
+ * 1. A message has been successfully sent to the IOP.
+ * 4. An unsolicited message was received from the IOP.
+ */
+
+void adb_iop_receive(struct iop_msg *msg, struct pt_regs *regs)
+{
+	struct adb_iopmsg amsg;
+	struct adb_request *req;
+	unsigned char packet[16];
+	int i;
+
+	req = current_req;
+
+	if (msg->status == IOP_MSGSTATUS_COMPLETE) {
+		if (req->reply_expected) {
+			adb_from_iopmsg(msg, &amsg, 1);
+			req->reply_len = amsg.count + 1;
+			req->reply[0] = ADB_PACKET;
+			for (i = 0 ; i < sizeof(amsg.data) ; i++) {
+				req->reply[i+1] = amsg.data[i];
+			}
+		}
+		req->complete = 1;
+		current_req = req->next;
+		if (req->done) (*req->done)(req);
+		adb_state = idle;
+	} else if (msg->status == IOP_MSGSTATUS_UNSOL) {
+		adb_from_iopmsg(msg, &amsg, 0);
+		if (amsg.flags & ADB_IOP_TIMEOUT) {	/* timeout */
+			printk("adb_iop_receive: timeout, retransmitting to %d\n", last_active);
+			if (last_active == -1)
+				last_active = (amsg.cmd&0xf0)>>4;
+			if (current_req) {
+				adb_start();
+			} else {
+				adb_retransmit(last_active);
+			}
+		} else {
+			/* fake a CUDA-format packet */
+			packet[0] = ADB_PACKET;	/* packet type  */
+			packet[1] = 0;		/* ????         */
+			packet[2] = amsg.cmd;	/* command byte */
+			i = 0;
+			while (i < amsg.count) {
+				packet[i+3] = amsg.data[i];
+				i++;
+			}
+			adb_input(packet, amsg.count + 3, regs);
+			memcpy(&msg->reply[0], &msg->message[0], IOP_MSG_LEN);
+		}
+		iop_complete_message(msg);
+		adb_state = idle;
+	} else {
+		printk("adb_iop_receive: unknown IOP message state %d.\n",
+			msg->status);
+	}
+}
+
 void adb_hw_setup_cuda(void)
 {
 	int		x;
@@ -398,20 +455,45 @@
 	printk("\nCUDA: HW Setup done!\n");
 }
 
-void adb_hw_setup_IIsi(void)
+static void adb_hw_setup_macII(void)
+{
+	/* Set the lines up. We want TREQ as input TACK|TIP as output */
+	via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
+	/*
+	 * Docs suggest TREQ should be output - that seems nuts
+	 * BSD agrees here :-)
+	 * Setup vPCR ??
+	 */
+
+	/* Set up state: idle */
+	via_write(via1, vBufB, via_read(via1, vBufB) | ST_IDLE);
+	last_status = (via_read(via1, vBufB)&~ST_MASK);
+
+	/* Shift register on input */
+	via_write(via1, vACR, (via_read(via1, vACR) & ~SR_CTRL) | SR_EXT);
+	/* Wipe any pending data and int */
+	via_read(via1, vSR);
+
+	/* This is interrupts on enable SR for keyboard */
+	via_write(via1, vIER, IER_SET|SR_INT); 
+	/* This clears the interrupt bit */
+	via_write(via1, vIFR, SR_INT);
+}
+
+static void adb_IIsi_cleanup(void)
 {
 	int dummy;
 	long poll_timeout;
 
-	printk("adb_IIsi: cleanup!\n");
+	printk(KERN_DEBUG "adb_IIsi: cleanup!\n");
 
 	/* ??? */
 	udelay(ADB_DELAY);
 
-	/* disable SR int. */
-	via_write(via1, vIER, IER_CLR|SR_INT);
 	/* set SR to shift in */
 	via_write(via1, vACR, via_read(via1, vACR ) & ~SR_OUT);
+	/* disable SR int. */
+	via_write(via1, vIER, IER_CLR|SR_INT);
 
 	/* this is required, especially on faster machines */
 	udelay(ADB_DELAY);
@@ -454,6 +536,44 @@
 	via_write(via1, vIER, IER_SET|SR_INT);
 }
 
+static void adb_hw_setup_IIsi(void)
+{
+	int i;
+
+	/* Set the lines up. We want TREQ as input TACK|TIP as output */
+	via_write(via1, vDirB, (via_read(via1,vDirB)|TACK|TIP) & ~TREQ);
+
+	/* Shift register on input */
+	via_write(via1, vACR, (via_read(via1, vACR) & ~SR_CTRL) | SR_EXT);
+	/* Wipe any pending data and int */
+	via_read(via1, vSR);
+
+	/* This is interrupts on enable SR for keyboard */
+	via_write(via1, vIER, IER_SET|SR_INT); 
+
+	/* Set initial state: idle */
+	via_write(via1, vBufB, via_read(via1, vBufB) & ~ST_IDLE);
+	last_status = (via_read(via1, vBufB)&~ST_MASK);
+
+	/* This clears the interrupt bit */
+	via_write(via1, vIFR, SR_INT);
+
+	/* get those pesky clock ticks we missed while booting */
+	for ( i = 0; i < 60; i++) {
+		udelay(ADB_DELAY);
+		adb_IIsi_cleanup();
+		udelay(ADB_DELAY);
+		if (via_read(via1, vBufB) & TREQ)
+			break;
+	}
+	if (i == 60)
+		printk("adb_IIsi: maybe bus jammed ??\n");
+}
+
+static void adb_hw_setup_pm(void)
+{
+}
+
 #define WAIT_FOR(cond, what)				\
     do {						\
 	for (x = 1000; !(cond); --x) {			\
@@ -477,7 +597,7 @@
  *	here depending on hardware type ...
  */
 int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
-	     int nbytes, ...)
+		int nbytes, ...)
 {
 	va_list list;
 	int i, start;
@@ -488,6 +608,9 @@
 	 * skip first byte if not CUDA 
 	 */
 	if (macintosh_config->adb_type == MAC_ADB_II) {
+/*
+	    (macintosh_config->adb_type == MAC_ADB_IOP)) {
+*/
 		start =  va_arg(list, int);
 		nbytes--;
 	}
@@ -599,7 +722,7 @@
 	r.reply_expected=0;
 	r.done=NULL;
 	r.sent=0;
-	r.got_reply=0;
+	r.complete=0;
 	r.reply_len=0;
 	save_flags(flags);
 	cli();
@@ -636,7 +759,7 @@
 	rt.reply_expected = 0;
 	rt.done           = NULL;
 	rt.sent           = 0;
-	rt.got_reply      = 0;
+	rt.complete      = 0;
 	rt.reply_len      = 0;
 	rt.next           = NULL;
 
@@ -674,7 +797,7 @@
 
 	req->next = 0;
 	req->sent = 0;
-	req->got_reply = 0;
+	req->complete = 0;
 	req->reply_len = 0;
 	save_flags(flags); 
 	cli();
@@ -696,14 +819,92 @@
 	return 0;
 }
 
-static int nclock, ndata;
+/*
+ * Start sending an ADB packet, IOP style
+ *
+ * There isn't much to do other than hand the packet over to the IOP
+ * after encapsulating it in an adb_iopmsg.
+ */
 
-static int need_poll    = 0;
-static int command_byte = 0;
-static int last_reply   = 0;
-static int last_active  = 0;
+static void adb_start_iop(void)
+{
+	unsigned long flags;
+	struct adb_request *req;
+	struct adb_iopmsg amsg;
+	int reply_expected,i,nb;
 
-static struct adb_request *retry_req;
+	/* get the packet to send */
+	req = current_req;
+
+	/* TODO: re-enable transmits when timeout handling is fixed */
+
+	req->sent = 1;
+	req->complete = 1;
+	if (req->done) (*req->done)(req);
+	return;
+
+	/* assert adb_state == idle */
+	if (adb_state != idle) {
+		printk("adb-iop: adb_start_iop called while driver busy (%p %x)!\n",
+			req, adb_state);
+		return;
+	}
+
+	if (req->data[0] != ADB_PACKET) {
+		printk("abb-iop: attempt to send non-ADB packet through IOP\n");
+		return;
+	}
+
+	if (req == 0) return;
+	save_flags(flags); 
+	cli();
+	
+	/* Always expecting a reply could be fatal since it could clog */
+	/* the receive channel, not to mention it won't reset the adb  */
+	/* state back to idle since adb_iop_receive won't be called.   */
+	/* It's best to play it safe; if we aren't sure then don't     */
+	/* set reply_expected; it won't work right but at least it     */
+	/* won't clog the ADB subsystem.                               */
+
+	if ((req->data[1] & 0x0C) == AIF_TALK) {
+		reply_expected = 1;
+	} else {
+		reply_expected = 0;
+	}
+
+	/* The IOP appears to take MacII-style packets, not CUDA packets */
+
+	nb = req->nbytes - 2;
+	if (nb > sizeof(amsg.data)) nb = sizeof(amsg.data);
+
+	printk("adb-iop: sending packet, %d bytes:", nb);
+
+	amsg.flags = ADB_IOP_EXPLICIT;
+	amsg.cmd   = req->data[1];
+	amsg.count = nb;
+
+	printk(" %02X", (uint) amsg.cmd);
+
+	i = 0;
+	while(i < nb) {
+		amsg.data[i] = req->data[i+2];
+		printk(" %02X", (uint) amsg.data[i]);
+		i++;
+	}
+	printk("\n");
+
+	/* Now send it. The IOP manager will call */
+	/* adb_iop_receive when  the reply comes  */
+	/* or when the send is completed if no    */
+	/* reply is expected.                     */
+
+	iop_send_message(ADB_IOP, ADB_CHAN, req,
+			 sizeof(amsg), (__u8 *) &amsg, adb_iop_receive);
+
+	adb_state = sending;
+
+	restore_flags(flags);
+}
 
 /*
  * Start sending ADB packet 
@@ -713,10 +914,15 @@
 	unsigned long flags;
 	struct adb_request *req;
 
+	if (macintosh_config->adb_type == MAC_ADB_IOP) {
+		adb_start_iop();
+		return;
+	}
+
 	/*
 	 * We get here on three 'sane' conditions:
 	 * 1) called from send_adb_request, if adb_state == idle
-	 * 2) called from within adb_interrupt, if adb_state == idle 
+	 * 2) called from within adb_interrupt, if ade_state == idle 
 	 *    (after receiving, or after sending a LISTEN) 
 	 * 3) called from within adb_interrupt, if adb_state == sending 
 	 *    and no reply is expected (immediate next command).
@@ -741,8 +947,10 @@
 		printk("adb_start: request %p ", req);
 #endif
 
+#ifdef DEBUG_ADB_INTS
 	nclock = 0;
 	ndata  = 0;
+#endif
 
 	/* 
 	 * IRQ signaled ?? (means ADB controller wants to send, or might 
@@ -926,6 +1134,7 @@
 	restore_flags(flags);
 }
 
+#ifdef DEBUG_ADB_INTS
 /*
  * Debugging gimmicks
  */
@@ -938,6 +1147,7 @@
 {
 	ndata++;
 }
+#endif
 
 /*
  * The notorious ADB interrupt handler - does all of the protocol handling, 
@@ -1345,7 +1555,7 @@
 						/* XXX: fake ADB header? */
 						req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;  
 						req->reply_len = 3;
-						req->got_reply = 1;
+						req->complete = 1;
 						current_req = req->next;
 						if (req->done)
 							(*req->done)(req);
@@ -1413,7 +1623,7 @@
 					/* invert state bits, toggle ODD/EVEN */
 					x = via_read(via1, vBufB);
 					via_write(via1, vBufB,
-						(x&~ST_MASK)|~(x&ST_MASK));
+						(x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK));
 				}
 			}
 			break;
@@ -1632,7 +1842,7 @@
 					/* invert state bits, toggle ODD/EVEN */
 					x = via_read(via1, vBufB);
 					via_write(via1, vBufB,
-						(x&~ST_MASK)|~(x&ST_MASK));
+						(x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK));
 #endif
 					/* adjust packet length */
 					reply_len--;
@@ -1655,7 +1865,7 @@
 						/* invert state bits, toggle ODD/EVEN */
 						x = via_read(via1, vBufB);
 						via_write(via1, vBufB,
-							(x&~ST_MASK)|~(x&ST_MASK));
+							(x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK));
 					}
 				}
 			}
@@ -1672,7 +1882,7 @@
 			{
 				req = current_req;
 				req->reply_len = reply_ptr - req->reply;
-				req->got_reply = 1;
+				req->complete = 1;
 				current_req = req->next;
 				if (req->done)
 					(*req->done)(req);
@@ -1824,14 +2034,6 @@
 				udelay(150);
 		 		via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
 			}
-			else if(macintosh_config->adb_type==MAC_ADB_II)
-			{
-				if (status != TREQ)
-					printk("adb_macII: state=idle status=%x want=%x\n",
-						status, TREQ);
-				x = via_read(via1, vSR);
-				via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
-			}
 			adb_state = reading;
 			reply_ptr = cuda_rbuf;
 			reply_len = 0;
@@ -1949,9 +2151,7 @@
 					/* delay */
 					udelay(ADB_DELAY);
 					/* set the shift register to shift out and send a byte */
-#if 0
 					via_write(via1, vACR, via_read(via1, vACR) | SR_OUT); 
-#endif
 					via_write(via1, vSR, current_req->data[1]);
 					/* signal 'byte ready' */
 					via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
@@ -1959,17 +2159,6 @@
 					adb_state = sending;
 				}
 			}
-			else if(macintosh_config->adb_type==MAC_ADB_II)
-			{
-				if(status!=TIP+SR_OUT)
-					printk("adb_macII: state=send_first_byte status=%x want=%x\n", 
-						status, TIP+SR_OUT);
-				via_write(via1, vSR, current_req->data[1]);
-				via_write(via1, vBufB,
-					via_read(via1, vBufB)^TACK);
-				data_index=2;			
-				adb_state = sending;
-			}
 			break;
 
 		case sending:
@@ -1997,21 +2186,13 @@
 					/* delay */
 					udelay(ADB_DELAY);
 					/* set the shift register to shift in */
-					via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
+					via_write(via1, vACR, via_read(via1, vACR) & ~SR_OUT); 
 					/* clear SR int. */
 					x = via_read(via1, vSR); 
 					/* set ADB state 'idle' (end of frame) */
 					via_write(via1, vBufB,
 						via_read(via1,vBufB) & ~(TACK|TIP)); 
 				}
-				else if(macintosh_config->adb_type==MAC_ADB_II)
-				{
-					via_write(via1, vACR,
-						via_read(via1, vACR) & ~SR_OUT);
-					x=via_read(via1, vSR);
-					via_write(via1, vBufB,
-						via_read(via1, vBufB)|TACK|TIP);
-				}
 				req->sent = 1;
 				if (req->reply_expected) 
 				{
@@ -2019,10 +2200,11 @@
 					 * maybe fake a reply here on Listen ?? 
 					 * Otherwise, a Listen hangs on success
 					 * CUDA+IIsi: only ADB Talk considered
-					 * RTC/PRAM read (0x1 0x3) to follow.
 					 */
 					if ( (req->data[0] == 0x0) && ((req->data[1]&0xc) == 0xc) )
 						adb_state = awaiting_reply;
+					else if ((req->data[0] == 0x1) && (req->data[1] == 0x3) )
+						adb_state = awaiting_reply;
 					else {
 						/*
 						 * Reply expected, but none
@@ -2036,7 +2218,7 @@
 						/* XXX: fake ADB header? */
 						req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;  
 						req->reply_len = 3;
-						req->got_reply = 1;
+						req->complete = 1;
 						current_req = req->next;
 						if (req->done)
 							(*req->done)(req);
@@ -2088,12 +2270,6 @@
 					/* signal 'byte ready' */
 					via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
 				}
-				else if(macintosh_config->adb_type==MAC_ADB_II)
-				{
-					via_write(via1, vSR, req->data[data_index++]);
-					via_write(via1, vBufB,
-						via_read(via1, vBufB)^TACK);
-				}
 			}
 			break;
 
@@ -2169,24 +2345,6 @@
 					   Handshake anyway?? */
 				}
 			}
-			if(macintosh_config->adb_type==MAC_ADB_II)
-			{
-				if( status == TIP)
-				{
-					via_write(via1, vBufB,
-						via_read(via1, vBufB)|TACK|TIP);
-					adb_state = read_done;
-				}
-				else
-				{
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-					if(status!=TIP+TREQ)
-						printk("macII_adb: state=reading status=%x\n", status);
-#endif
-					via_write(via1, vBufB, 
-						via_read(via1, vBufB)^TACK);
-				}
-			}
 			/* fall through for IIsi on end of frame */
 			if (macintosh_config->adb_type != MAC_ADB_IISI
 			    || adb_state != read_done)
@@ -2203,7 +2361,7 @@
 			{
 				req = current_req;
 				req->reply_len = reply_ptr - req->reply;
-				req->got_reply = 1;
+				req->complete = 1;
 				current_req = req->next;
 				if (req->done)
 					(*req->done)(req);
@@ -2247,31 +2405,38 @@
 
 }
 
+static void adb_pm_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	printk("adb_pm_interrupt called!\n");
+}
+
 /*
  *	The 'reply delivery' routine; determines which device sent the 
  *	request and calls the appropriate handler. 
  *	Reply data are expected in CUDA format (again, argh...) so we need
  *	to fake this in the interrupt handler for MacII.
+ *
+ *      Note!  The PowerPC code now expects data in ADB format, and
+ *      has their CUDA handler throw non-ADB packets on the floor.  We
+ *      are going to sync with them.  It is only a matter of time.
+ *
  *	Only one handler per device ID is currently possible.
  *	XXX: is the ID field here representing the default or real ID?
  */
 static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs)
 {
-	int i, id;
+	int id;
 
 	switch (buf[0]) 
 	{
 		case ADB_PACKET:
 			/* what's in buf[1] ?? */
+			/* according to via-cuda.c, buf[1] should tell
+			   us whether to autopoll or not */
 			id = buf[2] >> 4;
-#if 0
-			xmon_printf("adb packet: ");
-			for (i = 0; i < nb; ++i)
-				xmon_printf(" %x", buf[i]);
-			xmon_printf(", id = %d\n", id);
-#endif
 #if (ADBDEBUG & ADBDEBUG_INPUT)
 			if (console_loglevel == 10) {
+				int i;
 				printk("adb_input: adb packet ");
 				for (i = 0; i < nb; ++i)
 					printk(" %x", buf[i]);
@@ -2287,6 +2452,7 @@
 		default:
 #if (ADBDEBUG & ADBDEBUG_INPUT)
 			if (console_loglevel == 10) {
+				int i;
 				printk("adb_input: data from via (%d bytes):", nb);
 				for (i = 0; i < nb; ++i)
 					printk(" %.2x", buf[i]);
@@ -2338,7 +2504,7 @@
 	add_wait_queue(&adb_wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
-	while (!state->req.got_reply) {
+	while (!state->req.complete) {
 		if (file->f_flags & O_NONBLOCK) {
 			ret = -EAGAIN;
 			break;
@@ -2358,9 +2524,9 @@
 
 static void adb_write_done(struct adb_request *req)
 {
-	if (!req->got_reply) {
+	if (!req->complete) {
 		req->reply_len = 0;
-		req->got_reply = 1;
+		req->complete = 1;
 	}
 	wake_up_interruptible(&adb_wait);
 }
@@ -2371,7 +2537,7 @@
 
 static int adb_open(struct inode *inode, struct file *file)
 {
-	int adb_type, adb_subtype;
+	int adb_subtype;
 	struct adbdev_state *state;
 
 	if (MINOR(inode->i_rdev) > ADB_MAX_MINOR)
@@ -2401,28 +2567,29 @@
 	return 0;
 }
 
-static void adb_release(struct inode *inode, struct file *file)
+static int adb_release(struct inode *inode, struct file *file)
 {
 	struct adbdev_state *state = file->private_data;
 
 	if (state) {
+		int ret;
+		
 		file->private_data = NULL;
-		if (state->req.reply_expected && !state->req.got_reply)
-			if (adb_wait_reply(state, file))
-				return;
+		if (state->req.reply_expected && !state->req.complete)
+			if ((ret = adb_wait_reply(state, file)))
+				return ret;
 		kfree(state);
 	}
-	return;
+	return 0;
 }
 
-static int adb_lseek(struct inode *inode, struct file *file, 
-		     off_t offset, int origin)
+static loff_t adb_lseek(struct file *file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static int adb_read(struct inode *inode, struct file *file,
-		     char *buf, int count)
+static ssize_t adb_read(struct file *file, char *buf,
+			size_t count, loff_t *offset)
 {
 	int ret;
 	struct adbdev_state *state = file->private_data;
@@ -2449,8 +2616,8 @@
 	return ret;
 }
 
-static int adb_write(struct inode *inode, struct file *file,
-		      const char *buf, int count)
+static ssize_t adb_write(struct file *file, const char *buf,
+			 size_t count, loff_t *offset)
 {
 	int ret, i;
 	struct adbdev_state *state = file->private_data;
@@ -2461,7 +2628,7 @@
 	if (ret)
 		return ret;
 
-	if (state->req.reply_expected && !state->req.got_reply) {
+	if (state->req.reply_expected && !state->req.complete) {
 		/* A previous request is still being processed.
 		   Wait for it to finish. */
 		ret = adb_wait_reply(state, file);
@@ -2471,7 +2638,7 @@
 
 	state->req.nbytes = count;
 	state->req.done = adb_write_done;
-	state->req.got_reply = 0;
+	state->req.complete = 0;
 	copy_from_user(state->req.data, buf, count);
 #if 0
 	switch (adb_hardware) {
@@ -2508,7 +2675,12 @@
 	NULL,		/* no mmap */
 	adb_open,
 	NULL,		/* flush */
-	adb_release
+	adb_release,
+	NULL,		/* fsync */
+	NULL,		/* fasync */
+	NULL,		/* check_media_change */
+	NULL,		/* revalidate */
+	NULL		/* lock */
 };
 
 int adbdev_register(int subtype, struct file_operations *fops)
@@ -2537,7 +2709,6 @@
 		printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
 }
 
-
 #if 0 /* old ADB device */
 
 /*
@@ -2566,7 +2737,7 @@
 	add_wait_queue(&adb_wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
-	while (!state->req.got_reply) {
+	while (!state->req.complete) {
 		if (file->f_flags & O_NONBLOCK) {
 			ret = -EAGAIN;
 			break;
@@ -2586,9 +2757,9 @@
 
 static void adb_write_done(struct adb_request *req)
 {
-	if (!req->got_reply) {
+	if (!req->complete) {
 		req->reply_len = 0;
-		req->got_reply = 1;
+		req->complete = 1;
 	}
 	wake_up_interruptible(&adb_wait);
 }
@@ -2611,7 +2782,7 @@
 
 	if (state) {
 		file->private_data = NULL;
-		if (state->req.reply_expected && !state->req.got_reply)
+		if (state->req.reply_expected && !state->req.complete)
 			if (adb_wait_reply(state, file))
 				return;
 		kfree(state);
@@ -2619,14 +2790,13 @@
 	return;
 }
 
-static int adb_lseek(struct inode *inode, struct file *file,
-		     off_t offset, int origin)
+static int adb_lseek(struct file *file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static int adb_read(struct inode *inode, struct file *file,
-		    char *buf, int count)
+static int adb_read(struct file *file, char *buf, size_t count,
+		    loff_t *offset)
 {
 	int ret;
 	struct adbdev_state *state = file->private_data;
@@ -2653,8 +2823,8 @@
 	return ret;
 }
 
-static int adb_write(struct inode *inode, struct file *file,
-		     const char *buf, int count)
+static int adb_write(struct file *file, const char *buf,
+		     size_t count, loff_t *offset)
 {
 	int ret;
 	struct adbdev_state *state = file->private_data;
@@ -2665,7 +2835,7 @@
 	if (ret)
 		return ret;
 
-	if (state->req.reply_expected && !state->req.got_reply) {
+	if (state->req.reply_expected && !state->req.complete) {
 		/* A previous request is still being processed.
 		   Wait for it to finish. */
 		ret = adb_wait_reply(state, file);
@@ -2677,7 +2847,7 @@
 	state->req.done = adb_write_done;
 	memcpy_fromfs(state->req.data, buf, count);
 	state->req.reply_expected = 1;
-	state->req.got_reply = 0;
+	state->req.complete = 0;
 	adb_send_request(&state->req);
 
 	return count;
diff -urN linux-2.2.18-pre15/arch/m68k/mac/adb-misc.c linux/arch/m68k/mac/adb-misc.c
--- linux-2.2.18-pre15/arch/m68k/mac/adb-misc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/adb-misc.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,145 @@
+/*
+ * ADB Miscellaneous stuff
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#ifdef CONFIG_ADB_NEW
+#include <linux/adb.h>
+#else
+#include <asm/adb.h>
+#endif
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+
+extern void cuda_poll(void);
+extern int cuda_request(struct adb_request *req, 
+			void (*done)(struct adb_request *),
+			int nbytes, ...);
+
+/*
+ * Return the current time as the number of seconds since January 1, 1904.
+ *
+ * This method works for both CUDA and IIsi-style ADB. It'll probably
+ * work for PowerBooks too once we have PowerBook ADB working.
+ */
+
+__u32 adb_read_time(void)
+{
+	volatile struct adb_request req;
+	__u32 time;
+
+#ifdef CONFIG_ADB_NEW
+#ifdef CONFIG_ADB_CUDA
+	cuda_request((struct adb_request *) &req, NULL,
+			2, CUDA_PACKET, CUDA_GET_TIME);
+	while (!req.complete) cuda_poll();
+#endif
+#else
+	adb_request((struct adb_request *) &req, NULL,
+			2, CUDA_PACKET, CUDA_GET_TIME);
+	while (!req.complete);
+#endif
+
+	time = (req.reply[3] << 24) | (req.reply[4] << 16)
+		| (req.reply[5] << 8) | req.reply[6];
+	return time;
+}
+
+/*
+ * Set the current system time
+ *
+ * This method works for both CUDA and IIsi-style ADB. It'll probably
+ * work for PowerBooks too once we have PowerBook ADB working.
+ */
+
+void adb_write_time(__u32 data)
+{
+	volatile struct adb_request req;
+
+#ifdef CONFIG_ADB_NEW
+#ifdef CONFIG_ADB_CUDA
+	cuda_request((struct adb_request *) &req, NULL,
+			6, CUDA_PACKET, CUDA_SET_TIME,
+			(data >> 24) & 0xFF, (data >> 16) & 0xFF,
+			(data >> 8) & 0xFF, data & 0xFF);
+	while (!req.complete) cuda_poll();
+#endif
+#else
+	adb_request((struct adb_request *) &req, NULL,
+			6, CUDA_PACKET, CUDA_SET_TIME,
+			(data >> 24) & 0xFF, (data >> 16) & 0xFF,
+			(data >> 8) & 0xFF, data & 0xFF);
+	while (!req.complete);
+#endif
+}
+
+/*
+ * Initially discovered this technique in the Mach kernel of MkLinux in
+ * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c.  Found equivalent LinuxPPC
+ * code in arch/ppc/kernel/setup.c, which also has a PMU technique for
+ * PowerBooks!
+ *
+ * --David Kilzer
+ */
+
+void adb_poweroff(void)
+{
+	struct adb_request req;
+
+	/*
+	 * Print our "safe" message before we send the request
+	 * just in case the request never returns.
+	 */
+
+	printk ("It is now safe to switch off your machine.\n");
+
+#ifdef CONFIG_ADB_NEW
+#ifdef CONFIG_ADB_CUDA
+	cuda_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
+	for (;;) cuda_poll();
+#endif
+#else
+	adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
+	for (;;) ;
+#endif
+}
+
+/*
+ * Initially discovered this technique in the Mach kernel of MkLinux in
+ * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c.  Found equivalent LinuxPPC
+ * code in arch/ppc/kernel/setup.c, which also has a PMU technique!
+ * --David Kilzer
+ * 
+ * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines
+ * but have no way to test this myself.  --DDK
+ */
+
+void adb_hwreset(void)
+{
+	struct adb_request req;
+
+	printk ("Resetting system...\n");
+
+#ifdef CONFIG_ADB_NEW
+#ifdef CONFIG_ADB_CUDA
+	cuda_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
+	for (;;) cuda_poll();
+#endif
+#else
+	adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
+	for (;;) ;
+#endif
+}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/bootparse.c linux/arch/m68k/mac/bootparse.c
--- linux-2.2.18-pre15/arch/m68k/mac/bootparse.c	Thu Feb 12 16:30:13 1998
+++ linux/arch/m68k/mac/bootparse.c	Mon Dec  3 20:33:54 2001
@@ -1,6 +1,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <asm/irq.h>
 #include <asm/setup.h>
 #include <asm/bootinfo.h>
 #include <asm/macintosh.h>
diff -urN linux-2.2.18-pre15/arch/m68k/mac/config.c linux/arch/m68k/mac/config.c
--- linux-2.2.18-pre15/arch/m68k/mac/config.c	Tue Jan 19 10:58:34 1999
+++ linux/arch/m68k/mac/config.c	Mon Dec  3 20:33:54 2001
@@ -37,7 +37,20 @@
 #include <asm/macints.h>
 #include <asm/machw.h>
 
-#include "via6522.h"
+#include <asm/mac_iop.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+#include <asm/mac_psc.h>
+
+/* Offset between Unix time (1970-based) and Mac time (1904-based) */
+
+#define MAC_TIME_OFFSET 2082844800
+
+/*
+ * hardware reset vector
+ */
+
+static void (*rom_reset)(void);
 
 /* Mac bootinfo struct */
 
@@ -59,30 +72,20 @@
 unsigned long mac_orig_videoaddr;
 
 /* Mac specific keyboard functions */
-extern int mac_keyb_init(void);
-extern int mac_kbdrate(struct kbd_repeat *k);
-extern void mac_kbd_leds(unsigned int leds);
-
-/* Mac specific irq functions */
-extern void mac_init_IRQ (void);
-extern void (*mac_handlers[]) (int, void *, struct pt_regs *);
-extern int mac_request_irq (unsigned int irq,
-			    void (*handler)(int, void *, struct pt_regs *),
-                            unsigned long flags, const char *devname,
-			    void *dev_id);
-extern void mac_free_irq (unsigned int irq, void *dev_id);
-extern void mac_enable_irq (unsigned int);
-extern void mac_disable_irq (unsigned int);
-static void mac_get_model(char *model);
-/*static int mac_get_hardware_list(char *buffer);*/
-extern int mac_get_irq_list (char *);
+extern int mackbd_init_hw(void);
+extern void mackbd_leds(unsigned int leds);
 
 /* Mac specific timer functions */
 extern unsigned long mac_gettimeoffset (void);
 static void mac_gettod (int *, int *, int *, int *, int *, int *);
 static int mac_hwclk (int, struct hwclk_time *);
 static int mac_set_clock_mmss (unsigned long);
+extern void iop_init(void);
+extern void via_init(void);
 extern void via_init_clock(void (*func)(int, void *, struct pt_regs *));
+extern void via_flush_cache(void);
+extern void oss_init(void);
+extern void psc_init(void);
 
 extern void (*kd_mksound)(unsigned int, unsigned int);
 extern void mac_mksound(unsigned int, unsigned int);
@@ -95,6 +98,18 @@
 extern void mac_debug_init(void);
 extern void mac_debugging_long(int, long);
 
+/* poweroff functions */
+extern void via_poweroff(void);
+extern void oss_poweroff(void);
+extern void adb_poweroff(void);
+extern void adb_hwreset(void);
+
+/* pram functions */
+extern __u32 via_read_time(void);
+extern void via_write_time(__u32);
+extern __u32 adb_read_time(void);
+extern void adb_write_time(__u32);
+
 #ifdef CONFIG_MAGIC_SYSRQ
 static char mac_sysrq_xlate[128] =
 	"\000sdfghzxcv\000bqwer"				/* 0x00 - 0x0f */
@@ -124,92 +139,197 @@
 
 extern int console_loglevel;
 
-/*
- * This function translates the boot timeval into a proper date, to initialize
- * the system time.
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
  */
+static unsigned long mktime(unsigned int year, unsigned int mon,
+	unsigned int day, unsigned int hour,
+	unsigned int min, unsigned int sec)
+{
+	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
+		mon += 12;	/* Puts Feb last since it has leap day */
+		year -= 1;
+	}
+	return (((
+	    (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+	      year*365 - 719499
+	    )*24 + hour /* now have hours */
+	   )*60 + min /* now have minutes */
+	  )*60 + sec; /* finally seconds */
+}
 
-static void mac_gettod (int *yearp, int *monp, int *dayp,
-		 int *hourp, int *minp, int *secp)
+/*
+ * This function translates seconds since 1970 into a proper date.
+ *
+ * Algorithm cribbed from glibc2.1, __offtime().
+ */
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
+#define SECS_PER_DAY   (SECS_PER_HOUR * 24)
+
+static void unmktime(unsigned long time, long offset,
+		     int *yearp, int *monp, int *dayp,
+		     int *hourp, int *minp, int *secp)
 {
-	unsigned long time;
-	int leap, oldleap, isleap;
-	int mon_days[14] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, -1 };
-
-	time = mac_bi_data.boottime - 60*mac_bi_data.gmtbias; /* seconds */
-
-	*minp = time / 60;
-	*secp = time - (*minp * 60);
-	time  = *minp;				/* minutes now */
-
-	*hourp = time / 60;
-	*minp  = time - (*hourp * 60);
-	time   = *hourp;			/* hours now */
-
-	*dayp  = time / 24;
-	*hourp = time - (*dayp * 24);
-	time   = *dayp;				/* days now ... */
-
-	/* for leap day calculation */
-	*yearp = (time / 365) + 1970;		/* approx. year */
-
-	/* leap year calculation - there's an easier way, I bet. And it's broken :-( */
-	/* calculate leap days up to previous year */
-	oldleap =  (*yearp-1)/4 - (*yearp-1)/100 + (*yearp-1)/400;
-	/* calculate leap days incl. this year */
-	leap    =  *yearp/4 - *yearp/100 + *yearp/400;
-	/* this year a leap year ?? */
-	isleap  = (leap != oldleap);
-
-	/* adjust days: days, excluding past leap days since epoch */
-	time  -= oldleap - (1970/4 - 1970/100 + 1970/400);
-
-	/* precise year, and day in year */
-	*yearp = (time / 365);			/* #years since epoch */
-	*dayp  = time - (*yearp * 365) + 1;	/* #days this year (0: Jan 1) */
-	*yearp += 70;				/* add epoch :-) */
-	time = *dayp;
-	
-	if (isleap)				/* add leap day ?? */
-		mon_days[2] += 1;
-
-	/* count the months */
-	for (*monp = 1; time > mon_days[*monp]; (*monp)++) 
-		time -= mon_days[*monp];
+        /* How many days come before each month (0-12).  */
+	static const unsigned short int __mon_yday[2][13] =
+	{
+		/* Normal years.  */
+		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+		/* Leap years.  */
+		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+	};
+	long int days, rem, y, wday, yday;
+	const unsigned short int *ip;
+
+	days = time / SECS_PER_DAY;
+	rem = time % SECS_PER_DAY;
+	rem += offset;
+	while (rem < 0) {
+		rem += SECS_PER_DAY;
+		--days;
+	}
+	while (rem >= SECS_PER_DAY) {
+		rem -= SECS_PER_DAY;
+		++days;
+	}
+	*hourp = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	*minp = rem / SECS_PER_MINUTE;
+	*secp = rem % SECS_PER_MINUTE;
+	/* January 1, 1970 was a Thursday. */
+	wday = (4 + days) % 7; /* Day in the week. Not currently used */
+	if (wday < 0) wday += 7;
+	y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+#define __isleap(year)	\
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
 
-	*dayp = time;
+	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
+	{
+		/* Guess a corrected year, assuming 365 days per year.  */
+		long int yg = y + days / 365 - (days % 365 < 0);
 
+		/* Adjust DAYS and Y to match the guessed year.  */
+		days -= ((yg - y) * 365
+			 + LEAPS_THRU_END_OF (yg - 1)
+			 - LEAPS_THRU_END_OF (y - 1));
+		y = yg;
+	}
+	*yearp = y - 1900;
+	yday = days; /* day in the year.  Not currently used. */
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < (long int) ip[y]; --y)
+		continue;
+	days -= ip[y];
+	*monp = y;
+	*dayp = days + 1; /* day in the month */
 	return;
 }
 
+/*
+ * Return the boot time for use in initializing the kernel clock.
+ *
+ * I'd like to read the hardware clock here but many machines read
+ * the PRAM through ADB, and interrupts aren't initialized when this
+ * is called so ADB obviously won't work.
+ */
+
+static void mac_gettod(int *yearp, int *monp, int *dayp,
+		       int *hourp, int *minp, int *secp)
+{
+	/* Yes the GMT bias is backwards.  It looks like Penguin is
+           screwing up the boottime it gives us... This works for me
+           in Canada/Eastern but it might be wrong everywhere else. */
+	unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60,
+		yearp, monp, dayp, hourp, minp, secp);
+	/* For some reason this is off by one */
+	*monp = *monp + 1;
+}
+
 /* 
- * TBI: read and write hwclock
+ * Read/write the hardware clock.
  */
 
-static int mac_hwclk( int op, struct hwclk_time *t )
+static int mac_hwclk(int op, struct hwclk_time *t)
 {
-    return 0;
+	unsigned long now;
+
+	if (!op) { /* read */
+		if (macintosh_config->adb_type == MAC_ADB_II) {
+			now = via_read_time();
+		} else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+			   (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+			now = adb_read_time();
+		} else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+			now = via_read_time();
+		} else {
+			now = MAC_TIME_OFFSET;
+		}
+
+		now -= MAC_TIME_OFFSET;
+
+		t->wday = 0;
+		unmktime(now, 0,
+			 &t->year, &t->mon, &t->day,
+			 &t->hour, &t->min, &t->sec);
+	} else { /* write */
+		now = mktime(t->year + 1900, t->mon + 1, t->day,
+			     t->hour, t->min, t->sec) + MAC_TIME_OFFSET;
+
+		if (macintosh_config->adb_type == MAC_ADB_II) {
+			via_write_time(now);
+		} else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+			   (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+			adb_write_time(now);
+		} else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+			via_write_time(now);
+		}
+	}
+	return 0;
 }
 
 /*
- * TBI: set minutes/seconds in hwclock
+ * Set minutes/seconds in the hardware clock
  */
 
 static int mac_set_clock_mmss (unsigned long nowtime)
 {
-    short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+	struct hwclk_time now;
+
+	mac_hwclk(0, &now);
+	now.sec = nowtime % 60;
+	now.min = (nowtime / 60) % 60;
+	mac_hwclk(1, &now);
 
-    return 0;
+	return 0;
 }
 
+#if 0
 void mac_waitbut (void)
 {
 	;
 }
+#endif
 
 extern struct consw fb_con;
 extern struct fb_info *mac_fb_init(long *);
-extern void mac_video_setup(char *, int *);
+
+extern void mac_default_handler(int, void *, struct pt_regs *);
 
 void (*mac_handlers[8])(int, void *, struct pt_regs *)=
 {
@@ -237,7 +357,7 @@
 	    mac_bi_data.id = *data;
 	    break;
 	case BI_MAC_VADDR:
-	    mac_bi_data.videoaddr = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK);
+	    mac_bi_data.videoaddr = *data;
 	    break;
 	case BI_MAC_VDEPTH:
 	    mac_bi_data.videodepth = *data;
@@ -249,7 +369,8 @@
 	    mac_bi_data.dimensions = *data;
 	    break;
 	case BI_MAC_VLOGICAL:
-	    mac_bi_data.videological = *data;
+	    mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK);
+	    mac_orig_videoaddr = *data;
 	    break;
 	case BI_MAC_SCCBASE:
 	    mac_bi_data.sccbase = *data;
@@ -266,6 +387,9 @@
 	case BI_MAC_CPUID:
 	    mac_bi_data.cpuid = *data;
 	    break;
+        case BI_MAC_ROMBASE:
+	    mac_bi_data.rombase = *data;
+	    break;
 	default:
 	    unknown = 1;
     }
@@ -280,12 +404,11 @@
 
 static void mac_cache_card_flush(int writeback)
 {
-	unsigned long flags;
-	save_flags(flags);
+	unsigned long cpu_flags;
+	save_flags(cpu_flags);
 	cli();
-	via_write(via2, vBufB, via_read(via2,vBufB)&~VIA2B_vMode32);
-	via_write(via2, vBufB, via_read(via2,vBufB)|VIA2B_vMode32);
-	restore_flags(flags);
+	via_flush_cache();
+	restore_flags(cpu_flags);
 }
 
 __initfunc(void config_mac(void))
@@ -298,9 +421,8 @@
     mac_debug_init();
         
     mach_sched_init      = mac_sched_init;
-    mach_keyb_init       = mac_keyb_init;
-    mach_kbdrate         = mac_kbdrate;
-    mach_kbd_leds        = mac_kbd_leds;
+    mach_keyb_init       = mackbd_init_hw;
+    mach_kbd_leds        = mackbd_leds;
     mach_init_IRQ        = mac_init_IRQ;
     mach_request_irq     = mac_request_irq;
     mach_free_irq        = mac_free_irq;
@@ -319,11 +441,12 @@
     mach_mksound         = mac_mksound;
 #endif
     mach_reset           = mac_reset;
+    mach_halt            = mac_poweroff;
+    mach_power_off       = mac_poweroff;
     conswitchp	         = &dummy_con;
     mach_max_dma_address = 0xffffffff;
 #if 0
     mach_debug_init	 = mac_debug_init;
-    mach_video_setup	 = mac_video_setup;
 #endif
     kd_mksound		 = mac_mksound;
 #ifdef CONFIG_MAGIC_SYSRQ
@@ -346,18 +469,15 @@
     mac_identify();
     mac_report_hardware();
     
-    if(
-    	/* Cache cards */
-        macintosh_config->ident == MAC_MODEL_IICI||
-    	macintosh_config->ident == MAC_MODEL_IISI||
-    	macintosh_config->ident == MAC_MODEL_IICX||
-    	/* On board L2 cache */
-    	macintosh_config->ident == MAC_MODEL_IIFX)
-    {
+    /* AFAIK only the IIci takes a cache card.  The IIfx has onboard
+       cache ... someone needs to figure out how to tell if it's on or
+       not. */
+    if (macintosh_config->ident == MAC_MODEL_IICI
+	|| macintosh_config->ident == MAC_MODEL_IIFX) {
     	mach_l2_flush = mac_cache_card_flush;
     }
-    /* goes on forever if timers broken */
 #ifdef MAC_DEBUG_SOUND
+    /* goes on forever if timers broken */
     mac_mksound(1000,10);
 #endif
 
@@ -365,7 +485,9 @@
      * Check for machine specific fixups.
      */
 
+#ifdef OLD_NUBUS_CODE
     nubus_sweep_video();
+#endif
 }	
 
 
@@ -403,10 +525,11 @@
 	 *	The IIfx apparently has different ADB hardware, and stuff
 	 *	so zany nobody knows how to drive it.
 	 *	Even so, with Marten's help we'll try to deal with it :-)
+	 * CSA: see http://developer.apple.com/technotes/hw/hw_09.html
 	 */
 
 	{	MAC_MODEL_IICI,	"IIci",	MAC_ADB_II,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_IIFX,	"IIfx",	MAC_ADB_II,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_IIFX,	"IIfx",	MAC_ADB_IOP,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_IOP,	MAC_ETHER_NONE,	MAC_NUBUS},
 	{	MAC_MODEL_IISI, "IIsi",	MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
 	{	MAC_MODEL_IIVI,	"IIvi",	MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
 	{	MAC_MODEL_IIVX,	"IIvx",	MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
@@ -435,70 +558,72 @@
 	 *	confuse us. The 840AV has a SCSI location of its own (same as
 	 *	the 660AV).
 	 */	 
-
+	 
 	{	MAC_MODEL_Q605, "Quadra 605", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE,   MAC_SCC_QUADRA,	MAC_ETHER_NONE,		MAC_NUBUS},
+	{	MAC_MODEL_Q605_ACC, "Quadra 605 (33MHz)", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,		MAC_NUBUS},
 	{	MAC_MODEL_Q610, "Quadra 610", MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE,   MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
 	{	MAC_MODEL_Q630, "Quadra 630", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_QUADRA, MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
  	{	MAC_MODEL_Q650, "Quadra 650", MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE,   MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
 	/*	The Q700 does have a NS Sonic */
-	{	MAC_MODEL_Q700, "Quadra 700",   MAC_ADB_II,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_QUADRA2,	MAC_ETHER_SONIC,	MAC_NUBUS},
+	{	MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_QUADRA2,	MAC_ETHER_SONIC,	MAC_NUBUS},
 	{	MAC_MODEL_Q800, "Quadra 800", MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE,   MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
 	{	MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_MACE,		MAC_NUBUS},
-	/* These might have IOP problems */
-	{	MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_IOP,	MAC_ETHER_SONIC,	MAC_NUBUS},
-	{	MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_IOP,	MAC_ETHER_SONIC,	MAC_NUBUS},
+	{	MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_IOP,	MAC_ETHER_SONIC,	MAC_NUBUS},
+	{	MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE,   MAC_SCC_IOP,	MAC_ETHER_SONIC,	MAC_NUBUS},
 
 	/* 
 	 *	Performa - more LC type machines
 	 */
 
-	{	MAC_MODEL_P460,  "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci,   MAC_SCSI_OLD,    MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_P475,  "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE, MAC_NUBUS},
-	{	MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE, MAC_NUBUS},
-	{	MAC_MODEL_P520,  "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_P550,  "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci,   MAC_SCSI_OLD,    MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_P575,  "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_P588,  "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_TV,    "TV",           MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD,	MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_P600,  "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci,   MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-#if 0	/* other sources seem to suggest the P630/Q630/LC630 is more like LCIII */
-	{	MAC_MODEL_P630,  "Performa 630", MAC_ADB_IISI, MAC_VIA_IIci,   MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
-#endif
+	{	MAC_MODEL_P460,  "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci,   MAC_SCSI_OLD, 	MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_P475,  "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE, MAC_NUBUS},
+	{	MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE, MAC_NUBUS},
+	{	MAC_MODEL_P520,  "Performa 520", MAC_ADB_CUDA, MAC_VIA_IIci,   MAC_SCSI_OLD,    MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_P550,  "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci,   MAC_SCSI_OLD,    MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_P575,  "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE, MAC_NUBUS},
+	/* These have the comm slot, and therefore the possibility of SONIC ethernet */
+	{	MAC_MODEL_P588,  "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_II,	MAC_ETHER_SONIC, MAC_NUBUS},
+	{	MAC_MODEL_TV,    "TV",           MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD,	MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_P600,  "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci,   MAC_SCSI_OLD,	MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
+
 	/*
 	 *	Centris - just guessing again; maybe like Quadra
 	 */
 
-	{	MAC_MODEL_C610, "Centris 610",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC, MAC_NUBUS},
-	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC, MAC_NUBUS},
-	{	MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	/* The C610 may or may not have SONIC.  We probe to make sure */
+	{	MAC_MODEL_C610, "Centris 610",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
+	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC,	MAC_NUBUS},
+	{	MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_MACE,		MAC_NUBUS},
 
 	/*
 	 *      Power books - seem similar to early Quadras ? (most have 030 though)
 	 */
 
-	{	MAC_MODEL_PB140,  "PowerBook 140",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB145,  "PowerBook 145",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB140,  "PowerBook 140",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB145,  "PowerBook 145",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
 	/*	The PB150 has IDE, and IIci style VIA */
-	{	MAC_MODEL_PB150,  "PowerBook 150",   MAC_ADB_PB1, MAC_VIA_IIci,   MAC_SCSI_QUADRA, MAC_IDE_PB,	 MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB160,  "PowerBook 160",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB165,  "PowerBook 165",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB165C, "PowerBook 165c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB170,  "PowerBook 170",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,    MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB180,  "PowerBook 180",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB180C, "PowerBook 180c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB190,  "PowerBook 190",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB,   MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB520,  "PowerBook 520",   MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB150,  "PowerBook 150",   MAC_ADB_PB1, MAC_VIA_IIci,   MAC_SCSI_NONE, MAC_IDE_PB,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB160,  "PowerBook 160",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB165,  "PowerBook 165",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB165C, "PowerBook 165c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB170,  "PowerBook 170",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB180,  "PowerBook 180",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB180C, "PowerBook 180c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB190,  "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_PB,	MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	/* These have onboard SONIC */
+	{	MAC_MODEL_PB520,  "PowerBook 520",   MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,	MAC_SCC_QUADRA,	MAC_ETHER_SONIC, MAC_NUBUS},
 
 	/*
 	 *      Power book Duos - similar to Power books, I hope
 	 */
 
-	{	MAC_MODEL_PB210,  "PowerBook Duo 210",  MAC_ADB_PB2,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB230,  "PowerBook Duo 230",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD,    MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB250,  "PowerBook Duo 250",  MAC_ADB_PB2,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB280,  "PowerBook Duo 280",  MAC_ADB_PB2,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2,  MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	/* All of these might have onboard SONIC in the Dock but I'm not quite sure */
+	{	MAC_MODEL_PB210,  "PowerBook Duo 210",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB230,  "PowerBook Duo 230",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB250,  "PowerBook Duo 250",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB280,  "PowerBook Duo 280",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
 
 	/*
 	 *	Other stuff ??
@@ -533,7 +658,8 @@
 			model);
 		model = MAC_MODEL_Q800;
 		printk("Defaulting to: Quadra800, model id %d\n", model);
-		printk("Please report this case to linux-mac68k@wave.lm.com\n");
+		printk("Please report this case to "
+		       "linux-mac68k@mac.linux-m68k.org\n");
 		m=&mac_data_table[0];
 		while(m->ident != -1)
 		{
@@ -592,7 +718,10 @@
 	  break;
 
 	}
-	via_configure_base();
+	iop_init();
+	via_init();
+	oss_init();
+	psc_init();
 }
 
 void mac_report_hardware(void)
@@ -604,8 +733,133 @@
 {
 	strcpy(str,"Macintosh ");
 	strcat(str, macintosh_config->name);
-	if(mach_l2_flush && !(via_read(via2, vBufB)&VIA2B_vCDis))
-		strcat(str, "(+L2 cache)");
+}
+
+/*
+ *	The power switch - yes it's software!
+ */
+
+void mac_poweroff(void)
+{
+	/*
+	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+	 * work using the ADB packet method.  --David Kilzer
+	 */
+
+	if (oss_present) {
+		oss_poweroff();
+	} else if (macintosh_config->adb_type == MAC_ADB_II) {
+		via_poweroff();
+	} else {
+		adb_poweroff();
+	}
+}
+
+/* 
+ * Not all Macs support software power down; for the rest, just 
+ * try the ROM reset vector ...
+ */
+void mac_reset(void)
+{
+	/*
+	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+	 * work using the ADB packet method.  --David Kilzer
+	 */
+
+	if (macintosh_config->adb_type == MAC_ADB_II) {
+		unsigned long cpu_flags;
+
+		/* need ROMBASE in booter */
+		/* indeed, plus need to MAP THE ROM !! */
+
+		if (mac_bi_data.rombase == 0)
+			mac_bi_data.rombase = 0x40800000;
+
+		/* works on some */
+		rom_reset = (void *) (mac_bi_data.rombase + 0xa);
+
+		if (macintosh_config->ident == MAC_MODEL_SE30) {
+			/*
+			 * MSch: Machines known to crash on ROM reset ...
+			 */
+			printk("System halted.\n");
+			while(1);
+		} else {
+			save_flags(cpu_flags);
+			cli();
+
+			rom_reset();
+
+			restore_flags(cpu_flags);
+		}
+
+		/* We never make it this far... it usually panics above. */
+		printk ("Restart failed.  Please restart manually.\n");
+
+		/* XXX - delay do we need to spin here ? */
+		while(1);       /* Just in case .. */
+	} else if (macintosh_config->adb_type == MAC_ADB_IISI
+		|| macintosh_config->adb_type == MAC_ADB_CUDA) {
+		adb_hwreset();
+	} else if (CPU_IS_030) {
+
+		/* 030-specific reset routine.  The idea is general, but the
+		 * specific registers to reset are '030-specific.  Until I
+		 * have a non-030 machine, I can't test anything else.
+		 *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
+		 */
+
+		unsigned long rombase = 0x40000000;
+
+		/* make a 1-to-1 mapping, using the transparent tran. reg. */
+		unsigned long virt = (unsigned long) mac_reset;
+		unsigned long phys = virt_to_phys(mac_reset);
+		unsigned long offset = phys-virt;
+		cli(); /* lets not screw this up, ok? */
+		__asm__ __volatile__(".chip 68030\n\t"
+				     "pmove %0,%/tt0\n\t"
+				     ".chip 68k"
+				     : : "m" ((phys&0xFF000000)|0x8777));
+		/* Now jump to physical address so we can disable MMU */
+		__asm__ __volatile__(
+                    ".chip 68030\n\t"
+		    "lea %/pc@(1f),%/a0\n\t"
+		    "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
+		    "addl %0,%/sp\n\t" 
+		    "pflusha\n\t"
+		    "jmp %/a0@\n\t" /* jump into physical memory */
+		    "0:.long 0\n\t" /* a constant zero. */
+		    /* OK.  Now reset everything and jump to reset vector. */
+		    "1:\n\t"
+		    "lea %/pc@(0b),%/a0\n\t"
+		    "pmove %/a0@, %/tc\n\t" /* disable mmu */
+		    "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
+		    "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
+		    "movel #0, %/a0\n\t"
+		    "movec %/a0, %/vbr\n\t" /* clear vector base register */
+		    "movec %/a0, %/cacr\n\t" /* disable caches */
+		    "movel #0x0808,%/a0\n\t"
+		    "movec %/a0, %/cacr\n\t" /* flush i&d caches */
+		    "movew #0x2700,%/sr\n\t" /* set up status register */
+		    "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
+		    "movec %/a0, %/isp\n\t" 
+		    "movel %1@(0x4),%/a0\n\t" /* load reset vector */
+		    "reset\n\t" /* reset external devices */
+		    "jmp %/a0@\n\t" /* jump to the reset vector */
+		    ".chip 68k"
+		    : : "r" (offset), "a" (rombase) : "a0");
+
+		/* should never get here */
+		sti(); /* sure, why not */
+		printk ("030 Restart failed.  Please restart manually.\n");
+		while(1);
+	} else {
+		/* We never make it here... The above shoule handle all cases. */
+		printk ("Restart failed.  Please restart manually.\n");
+
+		/* XXX - delay do we need to spin here ? */
+		while(1);       /* Just in case .. */
+	}
 }
 
 /*
diff -urN linux-2.2.18-pre15/arch/m68k/mac/debug.c linux/arch/m68k/mac/debug.c
--- linux-2.2.18-pre15/arch/m68k/mac/debug.c	Tue Jan 19 10:58:34 1999
+++ linux/arch/m68k/mac/debug.c	Mon Dec  3 20:33:54 2001
@@ -243,7 +243,7 @@
     }
 }
 
-#ifdef CONFIG_SERIAL_CONSOLE
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_SERIAL)
 int mac_sccb_console_wait_key(struct console *co)
 {
     int i;
@@ -384,6 +384,16 @@
     mac_SCC_init_done = 1;
 }
 #endif /* DEBUG_SERIAL */
+
+void mac_init_scca_port( int cflag )
+{
+	mac_init_scc_port(cflag, 0);
+}
+
+void mac_init_sccb_port( int cflag )
+{
+	mac_init_scc_port(cflag, 1);
+}
 
 __initfunc(void mac_debug_init(void))
 {
diff -urN linux-2.2.18-pre15/arch/m68k/mac/iop.c linux/arch/m68k/mac/iop.c
--- linux-2.2.18-pre15/arch/m68k/mac/iop.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/iop.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,720 @@
+/*
+ * I/O Processor (IOP) management
+ * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice and this list of conditions.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice and this list of conditions in the documentation and/or other
+ *    materials provided with the distribution.
+ */
+
+/*
+ * The IOP chips are used in the IIfx and some Quadras (900, 950) to manage
+ * serial and ADB. They are actually a 6502 processor and some glue logic.
+ *
+ * 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP
+ *		  into compatible mode so nobody has to fiddle with the
+ *		  Serial Switch control panel anymore.
+ * 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS
+ *		  and non-OSS machines (at least I hope it's correct on a
+ *		  non-OSS machine -- someone with a Q900 or Q950 needs to
+ *		  check this.)
+ * 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is
+ *		  gone, IOP base addresses are now in an array and the
+ *		  globally-visible functions take an IOP number instead of an
+ *		  an actual base address.
+ * 990610 (jmt) - Finished the message passing framework and it seems to work.
+ *		  Sending _definately_ works; my adb-bus.c mods can send
+ *		  messages and receive the MSG_COMPLETED status back from the
+ *		  IOP. The trick now is figuring out the message formats.
+ * 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a
+ *		  receive channel were never properly acknowledged. Bracketed
+ *		  the remaining debug printk's with #ifdef's and disabled
+ *		  debugging. I can now type on the console.
+ * 990612 (jmt) - Copyright notice added. Reworked the way replies are handled.
+ *		  It turns out that replies are placed back in the send buffer
+ *		  for that channel; messages on the receive channels are always
+ *		  unsolicited messages from the IOP (and our replies to them
+ *		  should go back in the receive channel.) Also added tracking
+ *		  of device names to the listener functions ala the interrupt
+ *		  handlers.
+ * 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is
+ *		  used by the new unified ADB driver.
+ *
+ * TODO:
+ *
+ * o Something should be periodically checking iop_alive() to make sure the
+ *   IOP hasn't died.
+ * o Some of the IOP manager routines need better error checking and
+ *   return codes. Nothing major, just prettying up.
+ */
+
+/*
+ * -----------------------
+ * IOP Message Passing 101
+ * -----------------------
+ *
+ * The host talks to the IOPs using a rather simple message-passing scheme via
+ * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each
+ * channel is conneced to a specific software driver on the IOP. For example
+ * on the SCC IOP there is one channel for each serial port. Each channel has
+ * an incoming and and outgoing message queue with a depth of one.
+ *
+ * A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW,
+ * MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the
+ * buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag
+ * in the IOP control to 1. The IOP will move the state to MSG_RCVD when it
+ * receives the message and then to MSG_COMPLETE when the message processing
+ * has completed. It is the host's responsibility at that point to read the
+ * reply back out of the send channel buffer and reset the channel state back
+ * to MSG_IDLE.
+ *
+ * To receive message from the IOP the same procedure is used except the roles
+ * are reversed. That is, the IOP puts message in the channel with a state of
+ * MSG_NEW, and the host receives the message and move its state to MSG_RCVD
+ * and then to MSG_COMPLETE when processing is completed and the reply (if any)
+ * has been placed back in the receive channel. The IOP will then reset the
+ * channel state to MSG_IDLE.
+ *
+ * Two sets of host interrupts are provided, INT0 and INT1. Both appear on one
+ * interrupt level; they are distinguished by a pair of bits in the IOP status
+ * register. The IOP will raise INT0 when one or more messages in the send
+ * channels have gone to the MSG_COMPLETE state and it will raise INT1 when one
+ * or more messages on the receive channels have gone to the MSG_NEW state.
+ *
+ * Since each channel handles only one message we have to implement a small
+ * interrupt-driven queue on our end. Messages to e sent are placed on the
+ * queue for sending and contain a pointer to an optional callback function.
+ * The handler for a message is called when the message state goes to
+ * MSG_COMPLETE.
+ *
+ * For receiving message we maintain a list of handler functions to call when
+ * a message is received on that IOP/channel combination. The handlers are
+ * called much like an interrupt handler and are passed a copy of the message
+ * from the IOP. The message state will be in MSG_RCVD while the handler runs;
+ * it is the handler's responsibility to call iop_complete_message() when
+ * finished; this function moves the message state to MSG_COMPLETE and signals
+ * the IOP. This two-step process is provided to allow the handler to defer
+ * message processing to a bottom-half handler if the processing will take
+ * a signifigant amount of time (handlers are called at interrupt time so they
+ * should execute quickly.)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <asm/adb.h> 
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h> 
+#include <asm/mac_iop.h>
+#include <asm/mac_oss.h>
+
+/*#define DEBUG_IOP*/
+
+/* Set to nonezero if the IOPs are present. Set by iop_init() */
+
+int iop_scc_present,iop_ism_present;
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * sneaky reuse of the PROC_MAC_VIA inode. It's not needed by via.c
+ * anymore so we'll use it to debut the IOPs.
+ */
+
+int iop_get_proc_info(char *, char **, off_t, int, int);
+
+static struct proc_dir_entry proc_mac_iop = {
+	PROC_MAC_VIA, 7, "mac_iop",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations,
+	&iop_get_proc_info
+};
+
+#endif /* CONFIG_PROC_FS */
+
+/* structure for tracking channel listeners */
+
+struct listener {
+	const char *devname;
+	void (*handler)(struct iop_msg *, struct pt_regs *);
+};
+
+/*
+ * IOP structures for the two IOPs
+ *
+ * The SCC IOP controls both serial ports (A and B) as its two functions.
+ * The ISM IOP controls the SWIM (floppy drive) and ADB.
+ */
+
+static volatile struct mac_iop *iop_base[NUM_IOPS];
+
+/*
+ * IOP message queues
+ */
+
+static struct iop_msg iop_msg_pool[NUM_IOP_MSGS];
+static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN];
+static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN];
+
+void iop_ism_irq(int, void *, struct pt_regs *);
+
+extern void oss_irq_enable(int);
+
+/*
+ * Private access functions
+ */
+
+static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+}
+
+static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+	return iop->ram_data;
+}
+
+static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+	iop->ram_data = data;
+}
+
+static __inline__ void iop_stop(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl &= ~IOP_RUN;
+}
+
+static __inline__ void iop_start(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl = IOP_RUN | IOP_AUTOINC;
+}
+
+static __inline__ void iop_bypass(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl |= IOP_BYPASS;
+}
+
+static __inline__ void iop_interrupt(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl |= IOP_IRQ;
+}
+
+static int iop_alive(volatile struct mac_iop *iop)
+{
+	int retval;
+
+	retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF);
+	iop_writeb(iop, IOP_ADDR_ALIVE, 0);
+	return retval;
+}
+
+static struct iop_msg *iop_alloc_msg(void)
+{
+	int i;
+	ulong cpu_flags;
+
+	save_flags(cpu_flags);
+	cli();
+
+	for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
+		if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) {
+			iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING;
+			restore_flags(cpu_flags);
+			return &iop_msg_pool[i];
+		}
+	}
+
+	restore_flags(cpu_flags);
+	return NULL;
+}
+
+static void iop_free_msg(struct iop_msg *msg)
+{
+	msg->status = IOP_MSGSTATUS_UNUSED;
+}
+
+/*
+ * Initialize the IOPs, if present.
+ */
+
+__initfunc(void iop_init(void))
+{
+	int i;
+	long tmp = 0;
+	volatile long *iop_bogon = &tmp;
+
+	if (macintosh_config->scc_type == MAC_SCC_IOP) {
+		if (macintosh_config->ident == MAC_MODEL_IIFX) {
+			iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX;
+		} else {
+			iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA;
+		}
+
+		iop_scc_present = 1;
+
+		printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]);
+		iop_base[IOP_NUM_SCC]->status_ctrl = 0;
+		iop_bypass(iop_base[IOP_NUM_SCC]);
+	} else {
+		iop_base[IOP_NUM_SCC] = NULL;
+		iop_scc_present = 0;
+	}
+	if (macintosh_config->adb_type == MAC_ADB_IOP) {
+		if (macintosh_config->ident == MAC_MODEL_IIFX) {
+			iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX;
+		} else {
+			iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA;
+		}
+		iop_ism_present = 1;
+
+		printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]);
+		iop_base[IOP_NUM_SCC]->status_ctrl = 0;
+		for (i = 0 ; i < 16; i++) {
+			*iop_bogon = *iop_bogon;
+		}
+		iop_start(iop_base[IOP_NUM_ISM]);
+		iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */
+	} else {
+		iop_base[IOP_NUM_ISM] = NULL;
+		iop_ism_present = 0;
+	}
+
+	/* Make the whole pool available and empty the queues */
+
+	for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
+		iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED;
+	}
+
+	for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+		iop_send_queue[IOP_NUM_SCC][i] = 0;
+		iop_send_queue[IOP_NUM_ISM][i] = 0;
+		iop_listeners[IOP_NUM_SCC][i].devname = NULL;
+		iop_listeners[IOP_NUM_SCC][i].handler = NULL;
+		iop_listeners[IOP_NUM_ISM][i].devname = NULL;
+		iop_listeners[IOP_NUM_ISM][i].handler = NULL;
+	}
+
+#ifdef CONFIG_PROC_FS
+	proc_register(&proc_root, &proc_mac_iop);
+#endif
+}
+
+/*
+ * Register the interrupt handler for the IOPs.
+ * TODO: might be wrong for non-OSS machines. Anyone?
+ */
+
+__initfunc(void iop_register_interrupts(void))
+{
+	if (iop_ism_present) {
+		if (oss_present) {
+			sys_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
+					IRQ_FLG_LOCK, "ISM IOP",
+					(void *) IOP_NUM_ISM);
+			oss_irq_enable(IRQ_MAC_ADB);
+		} else {
+			request_irq(IRQ_VIA2_0, iop_ism_irq,
+					IRQ_FLG_LOCK|IRQ_FLG_FAST, "ISM IOP",
+					(void *) IOP_NUM_ISM);
+		}
+		if (!iop_alive(iop_base[IOP_NUM_ISM])) {
+			printk("IOP: oh my god, they killed the ISM IOP!\n");
+		} else {
+			printk("IOP: the ISM IOP seems to be alive.\n");
+		}
+	}
+}
+
+/*
+ * Register or unregister a listener for a specific IOP and channel
+ *
+ * If the handler pointer is NULL the current listener (if any) is
+ * unregistered. Otherwise the new listener is registered provided
+ * there is no existing listener registered.
+ */
+
+int iop_listen(uint iop_num, uint chan,
+		void (*handler)(struct iop_msg *, struct pt_regs *),
+		const char *devname)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
+	if (chan >= NUM_IOP_CHAN) return -EINVAL;
+	if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL;
+	iop_listeners[iop_num][chan].devname = devname;
+	iop_listeners[iop_num][chan].handler = handler;
+	return 0;
+}
+
+/*
+ * Complete reception of a message, which just means copying the reply
+ * into the buffer, setting the channel state to MSG_COMPLETE and
+ * notifying the IOP.
+ */
+
+void iop_complete_message(struct iop_msg *msg)
+{
+	int iop_num = msg->iop_num;
+	int chan = msg->channel;
+	int i,offset;
+
+#ifdef DEBUG_IOP
+	printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel);
+#endif
+
+	offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		iop_writeb(iop_base[iop_num], offset, msg->reply[i]);
+	}
+
+	iop_writeb(iop_base[iop_num],
+		   IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE);
+	iop_interrupt(iop_base[msg->iop_num]);
+
+	iop_free_msg(msg);
+}
+
+/*
+ * Actually put a message into a send channel buffer
+ */
+
+static void iop_do_send(struct iop_msg *msg)
+{
+	volatile struct mac_iop *iop = iop_base[msg->iop_num];
+	int i,offset;
+
+	offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		iop_writeb(iop, offset, msg->message[i]);
+	}
+
+	iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW);
+
+	iop_interrupt(iop);
+}
+
+/*
+ * Handle sending a message on a channel that
+ * has gone into the IOP_MSG_COMPLETE state.
+ */
+
+static void iop_handle_send(uint iop_num, uint chan, struct pt_regs *regs)
+{
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	struct iop_msg *msg,*msg2;
+	int i,offset;
+
+#ifdef DEBUG_IOP
+	printk("iop_handle_send: iop %d channel %d\n", iop_num, chan);
+#endif
+
+	iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE);
+
+	if (!(msg = iop_send_queue[iop_num][chan])) return;
+
+	msg->status = IOP_MSGSTATUS_COMPLETE;
+	offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN);
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		msg->reply[i] = iop_readb(iop, offset);
+	}
+	if (msg->handler) (*msg->handler)(msg, regs);
+	msg2 = msg;
+	msg = msg->next;
+	iop_free_msg(msg2);
+
+	iop_send_queue[iop_num][chan] = msg;
+	if (msg) iop_do_send(msg);
+}
+
+/*
+ * Handle reception of a message on a channel that has
+ * gone into the IOP_MSG_NEW state.
+ */
+
+static void iop_handle_recv(uint iop_num, uint chan, struct pt_regs *regs)
+{
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	int i,offset;
+	struct iop_msg *msg;
+
+#ifdef DEBUG_IOP
+	printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan);
+#endif
+
+	msg = iop_alloc_msg();
+	msg->iop_num = iop_num;
+	msg->channel = chan;
+	msg->status = IOP_MSGSTATUS_UNSOL;
+	msg->handler = iop_listeners[iop_num][chan].handler;
+
+	offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		msg->message[i] = iop_readb(iop, offset);
+	}
+
+	iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD);
+
+	/* If there is a listener, call it now. Otherwise complete */
+	/* the message ourselves to avoid possible stalls.         */
+
+	if (msg->handler) {
+		(*msg->handler)(msg, regs);
+	} else {
+#ifdef DEBUG_IOP
+		printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan);
+		printk("iop_handle_recv:");
+		for (i = 0 ; i < IOP_MSG_LEN ; i++) {
+			printk(" %02X", (uint) msg->message[i]);
+		}
+		printk("\n");
+#endif
+		iop_complete_message(msg);
+	}
+}
+
+/*
+ * Send a message
+ * 
+ * The message is placed at the end of the send queue. Afterwards if the
+ * channel is idle we force an immediate send of the next message in the
+ * queue.
+ */
+
+int iop_send_message(uint iop_num, uint chan, void *privdata,
+		      uint msg_len, __u8 *msg_data,
+		      void (*handler)(struct iop_msg *, struct pt_regs *))
+{
+	struct iop_msg *msg, *q;
+
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
+	if (chan >= NUM_IOP_CHAN) return -EINVAL;
+	if (msg_len > IOP_MSG_LEN) return -EINVAL;
+
+	msg = iop_alloc_msg();
+	if (!msg) return -ENOMEM;
+
+	msg->next = NULL;
+	msg->status = IOP_MSGSTATUS_WAITING;
+	msg->iop_num = iop_num;
+	msg->channel = chan;
+	msg->caller_priv = privdata;
+	memcpy(msg->message, msg_data, msg_len);
+	msg->handler = handler;
+
+	if (!(q = iop_send_queue[iop_num][chan])) {
+		iop_send_queue[iop_num][chan] = msg;
+	} else {
+		while (q->next) q = q->next;
+		q->next = msg;
+	}
+
+	if (iop_readb(iop_base[iop_num],
+	    IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) {
+		iop_do_send(msg);
+	}
+
+	return 0;
+}
+
+/*
+ * Upload code to the shared RAM of an IOP.
+ */
+
+void iop_upload_code(uint iop_num, __u8 *code_start,
+		     uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+	
+	while (code_len--) {
+		iop_base[iop_num]->ram_data = *code_start++;
+	}
+}
+
+/*
+ * Download code from the shared RAM of an IOP.
+ */
+
+void iop_download_code(uint iop_num, __u8 *code_start,
+		       uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+	
+	while (code_len--) {
+		*code_start++ = iop_base[iop_num]->ram_data;
+	}
+}
+
+/*
+ * Compare the code in the shared RAM of an IOP with a copy in system memory
+ * and return 0 on match or the first nonmatching system memory address on
+ * failure.
+ */
+
+__u8 *iop_compare_code(uint iop_num, __u8 *code_start,
+		       uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+	
+	while (code_len--) {
+		if (*code_start != iop_base[iop_num]->ram_data) {
+			return code_start;
+		}
+		code_start++;
+	}
+	return (__u8 *) 0;
+}
+
+/*
+ * Handle an ISM IOP interrupt
+ */
+
+void iop_ism_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	uint iop_num = (uint) dev_id;
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	int i,state;
+
+#ifdef DEBUG_IOP
+	printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl);
+#endif
+
+	/* INT0 indicates a state change on an outgoing message channel */
+
+	if (iop->status_ctrl & IOP_INT0) {
+		iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
+#ifdef DEBUG_IOP
+		printk("iop_ism_irq: new status = %02X, send states",
+			(uint) iop->status_ctrl);
+#endif
+		for (i = 0 ; i < NUM_IOP_CHAN  ; i++) {
+			state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
+#ifdef DEBUG_IOP
+			printk(" %02X", state);
+#endif
+			if (state == IOP_MSG_COMPLETE) {
+				iop_handle_send(iop_num, i, regs);
+			}
+		}
+#ifdef DEBUG_IOP
+		printk("\n");
+#endif
+	}
+
+	if (iop->status_ctrl & IOP_INT1) {	/* INT1 for incoming msgs */
+		iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
+#ifdef DEBUG_IOP
+		printk("iop_ism_irq: new status = %02X, recv states",
+			(uint) iop->status_ctrl);
+#endif
+		for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+			state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
+#ifdef DEBUG_IOP
+			printk(" %02X", state);
+#endif
+			if (state == IOP_MSG_NEW) {
+				iop_handle_recv(iop_num, i, regs);
+			}
+		}
+#ifdef DEBUG_IOP
+		printk("\n");
+#endif
+	}
+
+}
+
+#ifdef CONFIG_PROC_FS
+
+char *iop_chan_state(int state)
+{
+	switch(state) {
+		case IOP_MSG_IDLE	: return "idle      ";
+		case IOP_MSG_NEW	: return "new       ";
+		case IOP_MSG_RCVD	: return "received  ";
+		case IOP_MSG_COMPLETE	: return "completed ";
+		default			: return "unknown   ";
+	}
+}
+
+int iop_dump_one_iop(char *buf, int iop_num, char *iop_name)
+{
+	int i,len = 0;
+	volatile struct mac_iop *iop = iop_base[iop_num];
+
+	len += sprintf(buf+len, "%s IOP channel states:\n\n", iop_name);
+	len += sprintf(buf+len, "##  send_state  recv_state  device\n");
+	len += sprintf(buf+len, "------------------------------------------------\n");
+	for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+		len += sprintf(buf+len, "%2d  %10s  %10s  %s\n", i,
+			iop_chan_state(iop_readb(iop, IOP_ADDR_SEND_STATE+i)),
+			iop_chan_state(iop_readb(iop, IOP_ADDR_RECV_STATE+i)),
+			iop_listeners[iop_num][i].handler?
+				      iop_listeners[iop_num][i].devname : "");
+			
+	}
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+ 
+int iop_get_proc_info(char *buf, char **start, off_t pos, int count, int wr)
+{
+	int len, cnt;
+
+	cnt = 0;
+	len =  sprintf(buf, "IOPs detected:\n\n");
+
+	if (iop_scc_present) {
+		len += sprintf(buf+len, "SCC IOP (%p): status %02X\n",
+				iop_base[IOP_NUM_SCC],
+				(uint) iop_base[IOP_NUM_SCC]->status_ctrl);
+	}
+	if (iop_ism_present) {
+		len += sprintf(buf+len, "ISM IOP (%p): status %02X\n\n",
+				iop_base[IOP_NUM_ISM],
+				(uint) iop_base[IOP_NUM_ISM]->status_ctrl);
+	}
+
+	if (iop_scc_present) {
+		len += iop_dump_one_iop(buf+len, IOP_NUM_SCC, "SCC");
+
+	}
+
+	if (iop_ism_present) {
+		len += iop_dump_one_iop(buf+len, IOP_NUM_ISM, "ISM");
+
+	}
+
+	if (len >= pos) {
+		if (!*start) {
+			*start = buf + pos;
+			cnt = len - pos;
+		} else {
+			cnt += len;
+		}
+	}
+	return (count > cnt) ? cnt : count;
+}
+#endif /* CONFIG_PROC_FS */
diff -urN linux-2.2.18-pre15/arch/m68k/mac/mac_ksyms.c linux/arch/m68k/mac/mac_ksyms.c
--- linux-2.2.18-pre15/arch/m68k/mac/mac_ksyms.c	Wed Sep  2 09:39:18 1998
+++ linux/arch/m68k/mac/mac_ksyms.c	Mon Dec  3 20:33:54 2001
@@ -1,7 +1,14 @@
 #include <linux/module.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
+
 /* Hook for mouse driver */
 extern void (*adb_mouse_interrupt_hook) (char *);
 
+/* Says whether we're using A/UX interrupts or not */
+extern int via_alt_mapping;
+
+#ifndef CONFIG_ADB_NEW
 EXPORT_SYMBOL(adb_mouse_interrupt_hook);
+#endif
+EXPORT_SYMBOL(via_alt_mapping);
diff -urN linux-2.2.18-pre15/arch/m68k/mac/mac_penguin.S linux/arch/m68k/mac/mac_penguin.S
--- linux-2.2.18-pre15/arch/m68k/mac/mac_penguin.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/mac_penguin.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,75 @@
+.byte \
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,\
+0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,\
+0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,\
+0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,\
+0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00
diff -urN linux-2.2.18-pre15/arch/m68k/mac/macboing.c linux/arch/m68k/mac/macboing.c
--- linux-2.2.18-pre15/arch/m68k/mac/macboing.c	Tue Jan  5 11:20:43 1999
+++ linux/arch/m68k/mac/macboing.c	Mon Dec  3 20:33:54 2001
@@ -91,14 +91,8 @@
 			mac_special_bell = mac_quadra_start_bell;
 			mac_asc_samplespersec = 22150;
 			break;	
-		case MAC_MODEL_Q650:
-		case MAC_MODEL_Q700:
-		case MAC_MODEL_Q800:
-		case MAC_MODEL_Q900:
-		case MAC_MODEL_Q950:
-			/*
-			 * Currently not implemented!
-			 */
+		case MAC_MODEL_C660:
+		case MAC_MODEL_Q840:
 			/*
 			 * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O.
 			 * It appears to be similar to the "AWACS" custom ASIC in the Power Mac 
@@ -126,6 +120,22 @@
 			 */
 			mac_special_bell = mac_av_start_bell;
 			break;
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			/*
+			 * Currently not implemented!
+			 */
+			mac_special_bell = NULL;
+			break;
+		default:
+			/*
+			 * Every switch needs a default
+			 */
+			mac_special_bell = NULL;
+			break;
 	}
 
 	/* 
@@ -154,6 +164,12 @@
 	__u32 cfreq = ( freq << 5 ) / 468;
 	__u32 flags;
 	int i;
+
+	if ( mac_special_bell == NULL )
+	{
+		/* Do nothing */
+		return;
+	}
 
 	if ( !mac_asc_inited )
 		mac_init_asc();
diff -urN linux-2.2.18-pre15/arch/m68k/mac/macints.c linux/arch/m68k/mac/macints.c
--- linux-2.2.18-pre15/arch/m68k/mac/macints.c	Tue Jan  5 11:20:43 1999
+++ linux/arch/m68k/mac/macints.c	Mon Dec  3 20:33:54 2001
@@ -7,8 +7,8 @@
  * interrupts with exception vectors 0x19-0x1f). The following interrupt levels
  * are used:
  *	1	- VIA1
- *		  - slot 0: one second interrupt
- *		  - slot 1: VBlank
+ *		  - slot 0: one second interrupt (CA2)
+ *		  - slot 1: VBlank (CA1)
  *		  - slot 2: ADB data ready (SR full)
  *		  - slot 3: ADB data  (CB2)
  *		  - slot 4: ADB clock (CB1)
@@ -16,57 +16,103 @@
  *		  - slot 6: timer 1
  *		  - slot 7: status of IRQ; signals 'any enabled int.'
  *
- *	2	- VIA2, RBV or OSS
- *		  - slot 0: SCSI DRQ
- *		  - slot 1: NUBUS IRQ
- *		  - slot 3: SCSI IRQ
- *
- *	4	- SCC
- *		  - subdivided into Channel B and Channel A interrupts 
- *
- *	6	- Off switch (??)
- *
- *	7	- Debug output
- *
- * AV Macs only, handled by PSC:
- *
- *	3	- MACE ethernet IRQ (DMA complete on level 4)
- *
- *	5	- DSP ?? 
- *
- * Using the autovector irq numbers for Linux/m68k hardware interrupts without
- * the IRQ_MACHSPEC bit set would interfere with the general m68k interrupt 
- * handling in kernel versions 2.0.x, so the following strategy is used:
- *
- * - mac_init_IRQ installs the low-level entry points for the via1 and via2 
- *   exception vectors and the corresponding handlers (C functions); these 
- *   entry points just add the machspec bit and call the handlers proper.
- *   (in principle, the C functions can be installed as the exception vectors 
- *   directly, as they are hardcoded anyway; that's the current method). 
- *
- * - via[12]_irq determine what interrupt sources have triggered the interrupt,
- *   and call the corresponding device interrupt handlers. 
- *   (currently, via1_irq and via2_irq just call via_irq, passing the via base
- *   address. RBV interrupts are handled by (you guessed it) rbv_irq).
- *   Some interrupt functions want to have the interrupt number passed, so 
- *   via_irq and rbv_irq need to generate the 'fake' numbers from scratch.
- *
- * - for the request/free/enable/disable business, interrupt sources are 
- *   numbered internally (suggestion: keep irq 0-7 unused :-). One bit in the 
- *   irq number specifies the via# to use, i.e. via1 interrupts are 8-16, 
- *   via2 interrupts 17-32, rbv interrupts ...
- *   The device interrupt table and the irq_enable bitmap is maintained by 
- *   the machspec interrupt code; all device drivers should only use these 
- *   functions ! 
- *
- * - For future porting to version 2.1 (and removing of the machspec bit) it 
- *   should be sufficient to use the same numbers (everything > 7 is assumed 
- *   to be machspec, according to Jes!).
+ *	2	- VIA2 or RBV
+ *		  - slot 0: SCSI DRQ (CA2)
+ *		  - slot 1: NUBUS IRQ (CA1) need to read port A to find which
+ *		  - slot 2: /EXP IRQ (only on IIci)
+ *		  - slot 3: SCSI IRQ (CB2)
+ *		  - slot 4: ASC IRQ (CB1)
+ *		  - slot 5: timer 2 (not on IIci)
+ *		  - slot 6: timer 1 (not on IIci)
+ *		  - slot 7: status of IRQ; signals 'any enabled int.'
+ *
+ *	2	- OSS (IIfx only?)
+ *		  - slot 0: SCSI interrupt
+ *		  - slot 1: Sound interrupt
+ *
+ * Levels 3-6 vary by machine type. For VIA or RBV Macintohes:
+ *
+ *	3	- unused (?)
+ *
+ *	4	- SCC (slot number determined by reading RR3 on the SSC itself)
+ *		  - slot 0: SCC channel A
+ *		  - slot 1: SCC channel B
+ *
+ *	5	- unused (?)
+ *		  [serial errors or special conditions seem to raise level 6
+ *		  interrupts on some models (LC4xx?)]
+ *
+ *	6	- off switch (?)
+ *
+ * For OSS Macintoshes (IIfx only at this point):
+ *
+ *	3	- Nubus interrupt
+ *		  - slot 0: Slot $9
+ *		  - slot 1: Slot $A
+ *		  - slot 2: Slot $B
+ *		  - slot 3: Slot $C
+ *		  - slot 4: Slot $D
+ *		  - slot 5: Slot $E
+ *
+ *	4	- SCC IOP
+ *		  - slot 0: SCC channel A
+ *		  - slot 1: SCC channel B
+ *
+ *	5	- ISM IOP (ADB?)
+ *
+ *	6	- unused
+ *
+ * For PSC Macintoshes (660AV, 840AV):
+ *
+ *	3	- PSC level 3
+ *		  - slot 0: MACE
+ *
+ *	4	- PSC level 4
+ *		  - slot 1: SCC channel A interrupt
+ *		  - slot 2: SCC channel B interrupt
+ *		  - slot 3: MACE DMA
+ *
+ *	5	- PSC level 5
+ *
+ *	6	- PSC level 6
+ *
+ * Finally we have good 'ole level 7, the non-maskable interrupt:
+ *
+ *	7	- NMI (programmer's switch on the back of some Macs)
+ *		  Also RAM parity error on models which support it (IIc, IIfx?)
+ *
+ * The current interrupt logic looks something like this:
+ *
+ * - We install dispatchers for the autovector interrupts (1-7). These
+ *   dispatchers are responsible for querying the hardware (the
+ *   VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using
+ *   this information a machspec interrupt number is generated by placing the
+ *   index of the interrupt hardware into the low three bits and the original
+ *   autovector interrupt number in the upper 5 bits. The handlers for the
+ *   resulting machspec interrupt are then called.
+ *
+ * - Nubus is a special case because its interrupts are hidden behind two
+ *   layers of hardware. Nubus interrupts come in as index 1 on VIA #2,
+ *   which translates to IRQ number 17. In this spot we install _another_
+ *   dispatcher. This dispatcher finds the interrupting slot number (9-F) and
+ *   then forms a new machspec interrupt number as above with the slot number
+ *   minus 9 in the low three bits and the pseudo-level 7 in the upper five
+ *   bits.  The handlers for this new machspec interrupt number are then
+ *   called. This puts Nubus interrupts into the range 56-62.
+ *
+ * - We support "fast" and "slow" handlers, just like the Amiga port. The
+ *   fast handlers are called first and with all interrupts disabled. They
+ *   are expected to execute quickly (hence the name). The slow handlers are
+ *   called last with interrupts enabled and the interrupt level restored.
+ *   They must therefore be reentrant.
+ *
+ * - Drivers should never try to request autovector interrupt numbers. It
+ *   won't work.
  *
  *   TODO:
- * - integrate Nubus interrupts in request/free_irq
  *
- * - 
+ *   o Perhaps build some intelligence into mac_SCC_handler(); we could check
+ *     the SCC ourselves and only call the handler for the appopriate channel.
  */
 
 #include <linux/config.h>
@@ -82,124 +128,61 @@
 #include <asm/traps.h>
 #include <asm/machw.h>
 #include <asm/macintosh.h>
-#include "via6522.h"
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
 
 #include <asm/macints.h>
 
 /*
- * Interrupt handler and parameter types
+ * The mac_irq_list array is an array of linked lists of irq_node_t nodes.
+ * Each node contains one handler to be called whenever the interrupt
+ * occurs, with fast handlers listed before slow handlers.
  */
-struct irqhandler {
-	void	(*handler)(int, void *, struct pt_regs *);
-	void	*dev_id;
-};
-
-struct irqparam {
-	unsigned long	flags;
-	const char	*devname;
-};
-
-struct irqflags {
-        unsigned int disabled;
-	unsigned int pending;
-};
 
-/*
- * Array with irq's and their parameter data. 
- */
-static struct irqhandler  via1_handler[8];
-static struct irqhandler  via2_handler[8];
-static struct irqhandler   rbv_handler[8];
-static struct irqhandler  psc3_handler[8];
-static struct irqhandler   scc_handler[8];
-static struct irqhandler  psc5_handler[8];
-static struct irqhandler  psc6_handler[8];
-static struct irqhandler nubus_handler[8];
-
-static struct irqhandler *handler_table[8];
+irq_node_t *mac_irq_list[NUM_MAC_SOURCES];
 
 /*
- * This array hold the rest of parameters of int handlers: type
- * (slow,fast,prio) and the name of the handler. These values are only
- * accessed from C
+ * VIA/RBV hooks
  */
-static struct irqparam  via1_param[8];
-static struct irqparam  via2_param[8];
-static struct irqparam   rbv_param[8];
-static struct irqparam  psc3_param[8];
-static struct irqparam   scc_param[8];
-static struct irqparam  psc5_param[8];
-static struct irqparam  psc6_param[8];
-static struct irqparam nubus_param[8];
 
-static struct irqparam *param_table[8];
+extern void via_init(void);
+extern void via_register_interrupts(void);
+extern void via_irq_enable(int);
+extern void via_irq_disable(int);
+extern void via_irq_clear(int);
+extern int  via_irq_pending(int);
 
 /*
- * This array holds the 'disabled' and 'pending' software flags maintained
- * by mac_{enable,disable}_irq and the generic via_irq function.
+ * OSS hooks
  */
 
-static struct irqflags irq_flags[8];
+extern int oss_present;
 
-/*
- * This array holds the pointers to the various VIA or other interrupt 
- * controllers, indexed by interrupt level
- */
-
-static volatile unsigned char *via_table[8];
-
-/*
- * Arrays with irq statistics
- */
-static unsigned long via1_irqs[8];
-static unsigned long via2_irqs[8];
-static unsigned long rbv_irqs[8];
-static unsigned long psc3_irqs[8];
-static unsigned long scc_irqs[8];
-static unsigned long psc5_irqs[8];
-static unsigned long psc6_irqs[8];
-static unsigned long nubus_irqs[8];
-
-static unsigned long *mac_irqs[8];
-
-/*
- * Some special nutcases ...
- */
-
-static unsigned long mac_ide_irqs = 0;
-static unsigned long nubus_stuck_events = 0;
+extern void oss_init(void);
+extern void oss_register_interrupts(void);
+extern void oss_irq_enable(int);
+extern void oss_irq_disable(int);
+extern void oss_irq_clear(int);
+extern int  oss_irq_pending(int);
 
 /*
- * VIA/RBV/OSS/PSC register base pointers
+ * PSC hooks
  */
 
-volatile unsigned char *via2_regp=(volatile unsigned char *)VIA2_BAS;
-volatile unsigned char *rbv_regp=(volatile unsigned char *)VIA2_BAS_IIci;
-volatile unsigned char *oss_regp=(volatile unsigned char *)OSS_BAS;
-volatile unsigned char *psc_regp=(volatile unsigned char *)PSC_BAS;
-
-/*
- * Flags to control via2 / rbv behaviour
- */ 
-
-static int via2_is_rbv = 0;
-static int via2_is_oss = 0;
-static int rbv_clear = 0;
-
-/* fake VIA2 to OSS bit mapping */
-static int oss_map[8] = {2, 7, 0, 1, 3, 4, 5};
+extern int psc_present;
 
-void oss_irq(int irq, void *dev_id, struct pt_regs *regs);
-static void oss_do_nubus(int irq, void *dev_id, struct pt_regs *regs);
-
-/* PSC ints */
-void psc_irq(int irq, void *dev_id, struct pt_regs *regs);
+extern void psc_init(void);
+extern void psc_register_interrupts(void);
+extern void psc_irq_enable(int);
+extern void psc_irq_disable(int);
+extern void psc_irq_clear(int);
+extern int  psc_irq_pending(int);
 
 /*
- * PSC hooks
+ * IOP hooks
  */
 
-extern void psc_init(void);
+extern void iop_register_interrupts(void);
 
 /*
  * console_loglevel determines NMI handler function
@@ -207,640 +190,401 @@
 
 extern int console_loglevel;
 
-/*
- * ADB test hooks
- */
-extern int in_keybinit;
-void adb_queue_poll(void);
-
-/* Defined in entry.S; only increments 'num_spurious' */
-asmlinkage void bad_interrupt(void);
+extern void mac_bang(int, void *, struct pt_regs *);
 
-void nubus_wtf(int slot, void *via, struct pt_regs *regs);
-
-void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *regs);
-void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs);
-
-static void via_do_nubus(int slot, void *via, struct pt_regs *regs);
+void mac_nmi_handler(int, void *, struct pt_regs *);
+void mac_debug_handler(int, void *, struct pt_regs *);
+void mac_SCC_handler(int, void *, struct pt_regs *);
 
 /* #define DEBUG_MACINTS */
 
-#define DEBUG_SPURIOUS
-#define DEBUG_NUBUS_SPURIOUS
-#define DEBUG_NUBUS_INT
-
-/* #define DEBUG_VIA */
-#define DEBUG_VIA_NUBUS
-
 void mac_init_IRQ(void)
 {
         int i;
 
 #ifdef DEBUG_MACINTS
-	printk("Mac interrupt stuff initializing ...\n");
+	printk("mac_init_IRQ(): Setting things up...\n");
 #endif
+	/* Initialize the IRQ handler lists. Initially each list is empty, */
 
-	via2_regp = (unsigned char *)VIA2_BAS;
-	rbv_regp  = (unsigned char *)VIA2_BAS_IIci;
-
-        /* initialize the hardwired (primary, autovector) IRQs */
-
-	/* level 1 IRQ: VIA1, always present */
-	sys_request_irq(1, via1_irq, IRQ_FLG_LOCK, "via1", via1_irq);
-
-	/* via2 or rbv?? */
-	if (macintosh_config->via_type == MAC_VIA_IIci) {
-		/*
-		 * A word of caution: the definitions here only affect interrupt
-		 * handling, see via6522.c for yet another file to change
-		 * base addresses and RBV flags
-		 */
-
-		/* yes, this is messy - the IIfx deserves a class of his own */
-		if (macintosh_config->ident == MAC_MODEL_IIFX) {
-			/* no real VIA2, the OSS seems _very_ different */	
-			via2_is_oss = 1;
-			/* IIfx has OSS, at a different base address than RBV */
-			rbv_regp = (unsigned char *) OSS_BAS;
-			sys_request_irq(2, oss_irq, IRQ_FLG_LOCK, "oss", oss_irq);
-		} else {
-			/* VIA2 is part of the RBV: different base, other offsets */
-			via2_is_rbv = 1;
-
-			/* LC III weirdness: IFR seems to behave like VIA2 */
-			/* FIXME: maybe also for LC II ?? */ 
-			if (macintosh_config->ident == MAC_MODEL_LCIII) {
-				rbv_clear = 0x0;
-			} else {
-				rbv_clear = 0x80;
-			}
-			/* level 2 IRQ: RBV/OSS; we only care about RBV for now */
-			sys_request_irq(2, rbv_irq, IRQ_FLG_LOCK, "rbv", rbv_irq);
-		}
-	} else
-		/* level 2 IRQ: VIA2 */
-		sys_request_irq(2, via2_irq, IRQ_FLG_LOCK, "via2", via2_irq);
+	for (i = 0; i < NUM_MAC_SOURCES; i++) {
+		mac_irq_list[i] = NULL;
+	}
 
 	/* 
-	 * level 4 IRQ: SCC - use 'master' interrupt routine that calls the 
-	 *		registered channel-specific interrupts in turn.
-	 *		Currently, one interrupt per channel is used, solely
-	 *		to pass the correct async_info as parameter!
+	 * Now register the handlers for the the master IRQ handlers
+	 * at levels 1-7. Most of the work is done elsewhere.
 	 */
 
-	sys_request_irq(4, mac_debug_handler, IRQ_FLG_STD, "INT4", mac_debug_handler);
-
-	/* level 6 */
-	sys_request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang);
-
-	/* level 7 (or NMI) : debug stuff */
-	sys_request_irq(7, mac_nmi_handler, IRQ_FLG_STD, "NMI", mac_nmi_handler);
-
-	/* initialize the handler tables for VIAs */
-	for (i = 0; i < 8; i++) {
-		via1_handler[i].handler = mac_default_handler;
-		via1_handler[i].dev_id  = NULL;
-		via1_param[i].flags     = IRQ_FLG_STD;
-		via1_param[i].devname   = NULL;
-
-		via2_handler[i].handler = mac_default_handler;
-		via2_handler[i].dev_id  = NULL;
-		via2_param[i].flags     = IRQ_FLG_STD;
-		via2_param[i].devname   = NULL;
-
-		rbv_handler[i].handler = mac_default_handler;
-		rbv_handler[i].dev_id  = NULL;
-		rbv_param[i].flags     = IRQ_FLG_STD;
-		rbv_param[i].devname   = NULL;
-
-		scc_handler[i].handler = mac_default_handler;
-		scc_handler[i].dev_id  = NULL;
-		scc_param[i].flags     = IRQ_FLG_STD;
-		scc_param[i].devname   = NULL;
-
-		/* NUBUS interrupts routed through VIA2 slot 2 - special */
-		nubus_handler[i].handler = nubus_wtf;
-		nubus_handler[i].dev_id  = NULL;
-		nubus_param[i].flags     = IRQ_FLG_STD;
-		nubus_param[i].devname   = NULL;
-
-	}
-
-	/* initialize the handler tables (level 1 -> via_handler[0] !!!) */
-	via_table[0]     =  via1_regp;
-	handler_table[0] =  &via1_handler[0];
-	param_table[0]   =  &via1_param[0];
-	mac_irqs[0]	 =  &via1_irqs[0];
-
-	if (via2_is_rbv || via2_is_oss) {
-		via_table[1]     =  rbv_regp;
-		handler_table[1] =  &rbv_handler[0];
-		param_table[1]   =  &rbv_param[0];
-		mac_irqs[1]	 =  &rbv_irqs[0];
+	if (oss_present) {
+		oss_register_interrupts();
 	} else {
-		via_table[1]     =  via2_regp;
-		handler_table[1] =  &via2_handler[0];
-		param_table[1]   =  &via2_param[0];
-		mac_irqs[1]	 =  &via2_irqs[0];
+		via_register_interrupts();
 	}
-	via_table[2]     =  NULL;
-	via_table[3]     =  NULL;
-	
-	handler_table[2] =  &rbv_handler[0];
-	handler_table[3] =  &scc_handler[0];
-	handler_table[4] =  NULL;
-	handler_table[5] =  NULL;
-	handler_table[6] =  NULL;
-	handler_table[7] =  &nubus_handler[0];
-
-	param_table[2]   =  &rbv_param[0];
-	param_table[3]   =  &scc_param[0];
-	param_table[7]   =  &nubus_param[0];
-
-	mac_irqs[2]	 =  &rbv_irqs[0];
-	mac_irqs[3]	 =  &scc_irqs[0];
-	mac_irqs[7]	 =  &nubus_irqs[0];
-
-	/*
-	 * Nubus Macs: turn off the Nubus dispatch interrupt for now
-	 */
-
-	mac_turnoff_irq(IRQ_MAC_NUBUS);
-
-	/*
-	 *	AV Macs: shutup the PSC ints
-	 */
-	if (macintosh_config->ident == MAC_MODEL_C660
-	 || macintosh_config->ident == MAC_MODEL_Q840) 
-	{
-		psc_init();
-
-		handler_table[2] = &psc3_handler[0];
-		/* handler_table[3] = &psc4_handler[0]; */
-		handler_table[4] = &psc5_handler[0];
-		handler_table[5] = &psc6_handler[0];
-
-		param_table[2]   = &psc3_param[0];
-		/* param_table[3]   = &psc4_param[0]; */
-		param_table[4]   = &psc5_param[0];
-		param_table[5]   = &psc6_param[0];
-
-		mac_irqs[2]	 = &psc3_irqs[0]; 
-		/* mac_irqs[3]	 = &psc4_irqs[0]; */
-		mac_irqs[4]	 = &psc5_irqs[0];
-		mac_irqs[5]	 = &psc6_irqs[0];
-
-		sys_request_irq(3, psc_irq, IRQ_FLG_STD, "PSC3", psc_irq);
-		sys_request_irq(4, psc_irq, IRQ_FLG_STD, "PSC4", psc_irq);
-		sys_request_irq(5, psc_irq, IRQ_FLG_STD, "PSC5", psc_irq);
-		sys_request_irq(6, psc_irq, IRQ_FLG_STD, "PSC6", psc_irq);
-	}
-
+	if (psc_present) psc_register_interrupts();
+	iop_register_interrupts();
+	sys_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler);
 #ifdef DEBUG_MACINTS
-	printk("Mac interrupt init done!\n");
+	printk("mac_init_IRQ(): Done!\n");
 #endif
 }
 
 /*
- *	We have no machine specific interrupts on a macintoy
- *      Yet, we need to register/unregister interrupts ... :-)
- *      Currently unimplemented: Test for valid irq number, chained irqs,
- *      Nubus interrupts (use nubus_request_irq!).
+ * Routines to work with irq_node_t's on linked lists lifted from
+ * the Amiga code written by Roman Zippel.
  */
- 
-int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
-                              unsigned long flags, const char *devname, void *dev_id)
+
+static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node)
 {
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx =  (irq & IRQ_IDX_MASK);
-	struct irqhandler *via_handler;
-	struct irqparam   *via_param;
-	volatile unsigned char *via;
+	unsigned long cpu_flags;
+	irq_node_t *cur;
 
-#ifdef DEBUG_MACINTS
-	printk ("%s: IRQ %d on VIA%d[%d] requested from %s\n",
-	        __FUNCTION__, irq, srcidx+1, irqidx, devname);
-#endif
+	if (!node->dev_id)
+		printk("%s: Warning: dev_id of %s is zero\n",
+		       __FUNCTION__, node->devname);
 
-	if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) {
-		printk ("%s: Bad irq type %ld requested from %s\n",
-		        __FUNCTION__, flags, devname);
-		return -EINVAL;
-	}
+	save_flags(cpu_flags);
+	cli();
 
-	/* figure out what VIA is handling this irq */
-        if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) {
-		/* non-via irqs unimplemented */
-		printk ("%s: Bad irq source %d on VIA %d requested from %s\n",
-		        __FUNCTION__, irq, srcidx, devname);
-		return -EINVAL;
-	} 
-
-	/* figure out if SCC pseudo-irq (redundant ??) */
-        if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) {
-		/* set specific SCC handler */
-		scc_handler[irqidx].handler = handler;
-		scc_handler[irqidx].dev_id  = dev_id;
-		scc_param[irqidx].flags     = flags;
-		scc_param[irqidx].devname   = devname;
-		/* and done! */
-		return 0;
-	} 
+	cur = *list;
 
-	/* 
-	 * code below: only for VIA irqs currently 
-	 * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq
-	 */
-	via         = (volatile unsigned char *) via_table[srcidx];
-	if (!via) 
-		return -EINVAL;
-
-	via_handler = handler_table[srcidx];
-	via_param   = param_table[srcidx];
-
-	/* check for conflicts or possible replacement */
-
-	/* set the handler - no chained irqs yet !! */
-	via_handler[irqidx].handler = handler;
-	via_handler[irqidx].dev_id  = dev_id;
-	via_param[irqidx].flags     = flags;
-	via_param[irqidx].devname   = devname;
-
-	/* and turn it on ... careful, that's VIA only ... */
-	if (srcidx == SRC_VIA2 && via2_is_rbv)
-		via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx)));
-	else if (srcidx == SRC_VIA2 && via2_is_oss)
-		via_write(oss_regp, oss_map[irqidx]+8, 2);
-	else
-		via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx)));
-
-
-	if (irq == IRQ_IDX(IRQ_MAC_SCSI)) {
-		/*
-		 * Set vPCR for SCSI interrupts. (what about RBV here?)
-		 * 980429 MS: RBV is ok, OSS seems to be different
-		 */
-		if (!via2_is_oss)
-			if (macintosh_config->scsi_type == MAC_SCSI_OLD) {
-				/* CB2 (IRQ) indep. interrupt input, positive edge */
-				/* CA2 (DRQ) indep. interrupt input, positive edge */
-				via_write(via, vPCR, 0x66);
-			} else {
-				/* CB2 (IRQ) indep. interrupt input, negative edge */
-				/* CA2 (DRQ) indep. interrupt input, negative edge */
-				via_write(via, vPCR, 0x22);
-			}
-#if 0
-		else
-			/* CB2 (IRQ) indep. interrupt input, negative edge */
-			/* CA2 (DRQ) indep. interrupt input, negative edge */
-			via_write(via, vPCR, 0x22);
-#endif
+	if (node->flags & IRQ_FLG_FAST) {
+		node->flags &= ~IRQ_FLG_SLOW;
+		while (cur && cur->flags & IRQ_FLG_FAST) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else if (node->flags & IRQ_FLG_SLOW) {
+		while (cur) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else {
+		while (cur && !(cur->flags & IRQ_FLG_SLOW)) {
+			list = &cur->next;
+			cur = cur->next;
+		}
 	}
 
-	return 0;
+	node->next = cur;
+	*list = node;
+
+	restore_flags(cpu_flags);
 }
-                            
-void mac_free_irq (unsigned int irq, void *dev_id)
+
+static inline void mac_delete_irq(irq_node_t **list, void *dev_id)
 {
-	unsigned long flags;
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx =  (irq & IRQ_IDX_MASK);
-	struct irqhandler *via_handler;
-	struct irqparam   *via_param;
-	volatile unsigned char *via;
+	unsigned long cpu_flags;
+	irq_node_t *node;
 
-#ifdef DEBUG_MACINTS
-	printk ("%s: IRQ %d on VIA%d[%d] freed\n",
-	        __FUNCTION__, irq, srcidx+1, irqidx);
-#endif
+	save_flags(cpu_flags);
+	cli();
 
-	/* figure out what VIA is handling this irq */
-	if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) {
-		/* non-via irqs unimplemented */
-		return;
-	} 
+	for (node = *list; node; list = &node->next, node = *list) {
+		if (node->dev_id == dev_id) {
+			*list = node->next;
+			/* Mark it as free. */
+			node->handler = NULL;
+			restore_flags(cpu_flags);
+			return;
+		}
+	}
+	restore_flags(cpu_flags);
+	printk ("%s: tried to remove invalid irq\n", __FUNCTION__);
+}
 
-	save_flags(flags);
-	cli();
+/*
+ * Call all the handlers for a given interrupt. Fast handlers are called
+ * first followed by slow handlers.
+ *
+ * This code taken from the original Amiga code written by Roman Zippel.
+ */
 
-	/* figure out if SCC pseudo-irq */
-        if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) {
-		/* clear specific SCC handler */
-		scc_handler[irqidx].handler = mac_default_handler;
-		scc_handler[irqidx].dev_id  = NULL;
-		scc_param[irqidx].flags     = IRQ_FLG_STD;
-		scc_param[irqidx].devname   = NULL;
-		/* and done! */
-		restore_flags(flags);
-		return;
-	} 
+void mac_do_irq_list(int irq, struct pt_regs *fp)
+{
+	irq_node_t *node, *slow_nodes;
+	unsigned long cpu_flags;
 
-	via         = (volatile unsigned char *) via_table[srcidx];
-	via_handler = handler_table[srcidx];
-	via_param   = param_table[srcidx];
-
-	if ( !via || (via_handler[irqidx].dev_id != dev_id) ) {
-		restore_flags(flags);
-		goto not_found;
-	}
+	kstat.irqs[0][irq]++;
 
-	/* clear the handler - no chained irqs yet !! */
-	via_handler[irqidx].handler = mac_default_handler;
-	via_handler[irqidx].dev_id  = NULL;
-	via_param[irqidx].flags     = IRQ_FLG_STD;
-	via_param[irqidx].devname   = NULL;
-
-	/* and turn it off */
-	if (srcidx == SRC_VIA2 && via2_is_rbv)
-		via_write(via, rIER, (via_read(via, rIER)&(1<<irqidx)));
-	else if (srcidx == SRC_VIA2 && via2_is_oss)
-		via_write(oss_regp, oss_map[irqidx]+8, 0);
-	else
-		via_write(via, vIER, (via_read(via, vIER)&(1<<irqidx)));
-
-	restore_flags(flags);
-	return;
-
-not_found:
-	printk("%s: tried to remove invalid irq\n", __FUNCTION__);
-	return;
+#ifdef DEBUG_SPURIOUS
+	if (!mac_irq_list[irq] && (console_loglevel > 7)) {
+		printk("mac_do_irq_list: spurious interrupt %d!\n", irq);
+	}
+#endif
 
+	/* serve first fast and normal handlers */
+	for (node = mac_irq_list[irq];
+	     node && (!(node->flags & IRQ_FLG_SLOW));
+	     node = node->next)
+		node->handler(irq, node->dev_id, fp);
+	if (!node) return;
+	save_flags(cpu_flags);
+	restore_flags((cpu_flags & ~0x0700) | (fp->sr & 0x0700));
+	/* if slow handlers exists, serve them now */
+	slow_nodes = node;
+	for (; node; node = node->next) {
+		node->handler(irq, node->dev_id, fp);
+	}
 }
 
 /*
- * {en,dis}able_irq have the usual semantics of temporary blocking the
- * interrupt, but not loosing requests that happen between disabling and
- * enabling. On Atari, this is done with the MFP mask registers.
- *
- * On the Mac, this isn't possible: there is no VIA mask register. 
- * Needs to be implemented in software, setting 'mask' bits in a separate
- * struct for each interrupt controller. These mask bits need to be checked
- * by the VIA interrupt routine which should ignore requests for masked IRQs 
- * (after possibly ack'ing them).
- *
- * On second thought: some of the IRQ sources _can_ be turned off via bits
- * in the VIA output registers. Need to check this ...
- *
- * TBI: According to the VIA docs, clearing a bit in the IER has the effect of 
- * blocking generation of the interrupt, but the internal interrupt condition 
- * is preserved. So the IER might be used as mask register here, and turnon_irq
- * would need to clear the interrupt bit in the IFR to prevent getting an 
- * interrupt at all.
- *
- * Implementation note: the irq no's here are the _machspec_ irqs, hence the 
- * hack with srcidx to figure out which VIA/RBV handles the interrupt. 
- * That's fundamentally different when it comes to the interrupt handlers 
- * proper: these get the interrupt level no. as argument, all information about
- * which source triggered the int. is buried in the VIA IFR ... The int. level 
- * points us to the proper handler, so we could do a sanity check there ...
+ *  mac_enable_irq - enable an interrupt source
+ * mac_disable_irq - disable an interrupt source
+ *   mac_clear_irq - clears a pending interrupt
+ * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending)
+ *
+ * These routines are just dispatchers to the VIA/OSS/PSC routines.
  */
 
 void mac_enable_irq (unsigned int irq)
 {
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
-
-	irq_flags[srcidx].disabled &= ~(1<<irqidx);
-	/*
-	 * Call handler here if irq_flags[srcidx].pending & 1<<irqidx ?? 
-	 * The structure of via_irq prevents this, sort of: it warns if
-	 * no true events are pending. Maybe that's being changed ...
-	 * Other problem: is it always possible to call an interrupt handler, 
-	 * or should that depend on the current interrupt level?
-	 */
+	switch(IRQ_SRC(irq)) {
+		case 1: via_irq_enable(irq);
+			break;
+		case 2:
+		case 7: if (oss_present) {
+				oss_irq_enable(irq);
+			} else {
+				via_irq_enable(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_enable(irq);
+			} else if (oss_present) {
+				oss_irq_enable(irq);
+			}
+			break;
+	}
 }
 
 void mac_disable_irq (unsigned int irq)
 {
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
+	switch(IRQ_SRC(irq)) {
+		case 1: via_irq_disable(irq);
+			break;
+		case 2:
+		case 7:
+			if (oss_present) {
+				oss_irq_disable(irq);
+			} else {
+				via_irq_disable(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_disable(irq);
+			} else if (oss_present) {
+				oss_irq_disable(irq);
+			}
+			break;
+	}
+}
 
-	irq_flags[srcidx].disabled |= (1<<irqidx);
+void mac_clear_irq( unsigned int irq )
+{
+	switch(IRQ_SRC(irq)) {
+		case 1: via_irq_clear(irq);
+			break;
+		case 2:
+		case 7: if (oss_present) {
+				oss_irq_clear(irq);
+			} else {
+				via_irq_clear(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_clear(irq);
+			} else if (oss_present) {
+				oss_irq_clear(irq);
+			}
+			break;
+	}
+}
+
+int mac_irq_pending( unsigned int irq )
+{
+	switch(IRQ_SRC(irq)) {
+		case 1: return via_irq_pending(irq);
+		case 2:
+		case 7: if (oss_present) {
+				return oss_irq_pending(irq);
+			} else {
+				return via_irq_pending(irq);
+			}
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				return psc_irq_pending(irq);
+			} else if (oss_present) {
+				return oss_irq_pending(irq);
+			}
+	}
+	return 0;
 }
 
 /*
- * In opposite to {en,dis}able_irq, requests between turn{off,on}_irq are not
- * "stored". This is done with the VIA interrupt enable register on VIAs.
+ * Add an interrupt service routine to an interrupt source.
+ * Returns 0 on success.
  *
- * Note: Using these functions on non-VIA/OSS/PSC ints will panic, or at least 
- * have undesired side effects.
+ * FIXME: You can register interrupts on nonexistant source (ie PSC4 on a
+ *        non-PSC machine). We should return -EINVAL in those cases.
  */
-
-void mac_turnon_irq( unsigned int irq )
+ 
+int mac_request_irq(unsigned int irq,
+		    void (*handler)(int, void *, struct pt_regs *),
+		    unsigned long flags, const char *devname, void *dev_id)
 {
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
-	volatile unsigned char *via;
+	irq_node_t *node;
 
-	via         = (volatile unsigned char *) via_table[srcidx];
-	if (!via) 
-		return;
+#ifdef DEBUG_MACINTS
+	printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname);
+#endif
 
-	if (srcidx == SRC_VIA2 && via2_is_rbv)		/* RBV as VIA2 */
-	        via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx)));
-	else if (srcidx == SRC_VIA2 && via2_is_oss)	/* OSS */
-		via_write(oss_regp, oss_map[irqidx]+8, 2);
-	else if (srcidx > SRC_VIA2)			/* hope AVs have VIA2 */
-	        via_write(via, (0x104 + 0x10*srcidx), 
-	        	via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx)));
-	else						/* VIA1+2 */
-	        via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx)));
+	if (irq < VIA1_SOURCE_BASE) {
+		return sys_request_irq(irq, handler, flags, devname, dev_id);
+	}
 
-}
+	if (irq >= NUM_MAC_SOURCES) {
+		printk ("%s: unknown irq %d requested by %s\n",
+		        __FUNCTION__, irq, devname);
+	}
 
-void mac_turnoff_irq( unsigned int irq )
-{
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
-	volatile unsigned char *via;
+	/* Get a node and stick it onto the right list */
 
-	via         = (volatile unsigned char *) via_table[srcidx];
-	if (!via) 
-		return;
+	if (!(node = new_irq_node())) return -ENOMEM;
 
-	if (srcidx == SRC_VIA2 && via2_is_rbv)		/* RBV as VIA2 */
-		via_write(via, rIER, (via_read(via, rIER)&(1<<irqidx)));
-	else if (srcidx == SRC_VIA2 && via2_is_oss)	/* OSS */
-		via_write(oss_regp, oss_map[irqidx]+8, 0);
-	/*
-	 *	VIA2 is fixed. The stuff above VIA2 is for later
-	 *	macintoshes only.
-	 */
-	else if (srcidx > SRC_VIA2)
-	        via_write(via, (0x104 + 0x10*srcidx), 
-	        	via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx)));
-	else						/* VIA1+2 */
-		via_write(via, vIER, (via_read(via, vIER)&(1<<irqidx)));
-}
+	node->handler	= handler;
+	node->flags	= flags;
+	node->dev_id	= dev_id;
+	node->devname	= devname;
+	node->next	= NULL;
+	mac_insert_irq(&mac_irq_list[irq], node);
 
-/*
- * These functions currently only handle the software-maintained irq pending
- * list for disabled irqs - manipulating the actual irq flags in the via would
- * require clearing single bits in the via, such as (probably) 
- * via_write(via, vIFR, (via_read(via, vIFR)&(1<<irqidx))); - don't know if 
- * this has side-effects ...
- */
+	/* Now enable the IRQ source */
 
-void mac_clear_pending_irq( unsigned int irq )
-{
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
+	mac_enable_irq(irq);
 
-	irq_flags[srcidx].pending &= ~(1<<irqidx);
+	return 0;
 }
+                            
+/*
+ * Removes an interrupt service routine from an interrupt source.
+ */
 
-int  mac_irq_pending( unsigned int irq )
+void mac_free_irq(unsigned int irq, void *dev_id)
 {
-	int pending = 0;
-	volatile unsigned char *via;
+#ifdef DEBUG_MACINTS
+	printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id);
+#endif
 
-	int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1;
-	int irqidx = (irq & IRQ_IDX_MASK);
+	if (irq < VIA1_SOURCE_BASE) {
+		return sys_free_irq(irq, dev_id);
+	}
+
+	if (irq >= NUM_MAC_SOURCES) {
+		printk ("%s: unknown irq %d freed\n",
+		        __FUNCTION__, irq);
+		return;
+	}
 
-	pending = irq_flags[srcidx].pending & (1<<irqidx);
+	mac_delete_irq(&mac_irq_list[irq], dev_id);
 
-	via         = (volatile unsigned char *) via_table[srcidx];
-	if (!via) 
-		return (pending);
-
-	if (srcidx == SRC_VIA2 && via2_is_rbv)
-		pending |= via_read(via, rIFR)&(1<<irqidx);
-	else if (srcidx == SRC_VIA2 && via2_is_oss)
-		pending |= via_read(via, oIFR)&0x03&(1<<oss_map[irqidx]);
-	else if (srcidx > SRC_VIA2)
-	        pending |= via_read(via, (0x100 + 0x10*srcidx))&(1<<irqidx);
-	else
-		pending |= via_read(via, vIFR)&(1<<irqidx);
+	/* If the list for this interrupt is */
+	/* empty then disable the source.    */
 
-	return (pending);
+	if (!mac_irq_list[irq]) {
+		mac_disable_irq(irq);
+	}
 }
 
 /*
- * for /proc/interrupts: log interrupt stats broken down by 
- * autovector int first, then by actual interrupt source.
+ * Generate a pretty listing for /proc/interrupts
+ *
+ * By the time we're called the autovector interrupt list has already been
+ * generated, so we just need to do the machspec interrupts.
+ *
+ * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers.
+ *                Also removed display of num_spurious it is already
+ *		  displayed for us as autovector irq 0.
  */
 
 int mac_get_irq_list (char *buf)
 {
 	int i, len = 0;
-	int srcidx, irqidx;
+	irq_node_t *node;
+	char *base;
 
-	for (i = VIA1_SOURCE_BASE; i < NUM_MAC_SOURCES+8; ++i) {
-	        /* XXX fixme: IRQ_SRC_MASK should cover VIA1 - Nubus */
-		srcidx = ((i & IRQ_SRC_MASK)>>3) - 1;
-		irqidx = (i & IRQ_IDX_MASK);
-
-		/*
-		 * Not present: skip
-		 */
-
-		if (mac_irqs[srcidx] == NULL)
-			continue;
-
-		/* 
-		 * never used by VIAs, unused by others so far, counts 
-		 * the magic 'nothing pending' cases ...
-		 */
-		if (irqidx == 7 && mac_irqs[srcidx][irqidx]) {
-			len += sprintf(buf+len, "Level %01d: %10lu (spurious) \n",
-				       srcidx, 
-				       mac_irqs[srcidx][irqidx]);
-			continue;
-		}
+	/* Don't do Nubus interrupts in this loop; we do them separately  */
+	/* below so that we can print slot numbers instead of IRQ numbers */
 
-		/*
-		 * Nothing registered for this IPL: skip
-		 */
-
-		if (handler_table[srcidx] == NULL)
-			continue;
-
-		/*
-		 * No handler installed: skip
-		 */ 
-
-		if (handler_table[srcidx][irqidx].handler == mac_default_handler ||
-		    handler_table[srcidx][irqidx].handler == nubus_wtf)
-			continue;
-
-
-		if (i < VIA2_SOURCE_BASE)
-			len += sprintf(buf+len, "via1  %01d: %10lu ",
-				       irqidx,
-				       mac_irqs[srcidx][irqidx]);
-		else if (i < RBV_SOURCE_BASE)
-			len += sprintf(buf+len, "via2  %01d: %10lu ",
-				       irqidx,
-				       mac_irqs[srcidx][irqidx]);
-		else if (i < MAC_SCC_SOURCE_BASE)
-			len += sprintf(buf+len, "rbv   %01d: %10lu ",
-				       irqidx,
-				       mac_irqs[srcidx][irqidx]);
-		else if (i < NUBUS_SOURCE_BASE)
-			len += sprintf(buf+len, "scc   %01d: %10lu ",
-				       irqidx,
-				       mac_irqs[srcidx][irqidx]);
-		else /* Nubus */
-			len += sprintf(buf+len, "nubus %01d: %10lu ",
-				       irqidx,
-				       mac_irqs[srcidx][irqidx]);
+	for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) {
 
-			len += sprintf(buf+len, "%s\n", 
-				       param_table[srcidx][irqidx].devname);
+		/* Nonexistant interrupt or nothing registered; skip it. */
 
-	}
-	if (num_spurious)
-		len += sprintf(buf+len, "spurio.: %10u\n", num_spurious);
+		if ((node = mac_irq_list[i]) == NULL) continue;
+		if (node->flags & IRQ_FLG_STD) continue;
 
-	/* 
-	 * XXX Fixme: Nubus sources are never logged above ...
-	 */
-
-	len += sprintf(buf+len, "Nubus interrupts:\n");
-
-	for (i = 0; i < 7; i++) {
-		if (nubus_handler[i].handler == nubus_wtf)
-			continue;
-		len += sprintf(buf+len, "nubus %01X: %10lu ",
-			       i+9, 
-			       nubus_irqs[i]);
-		len += sprintf(buf+len, "%s\n", 
-			       nubus_param[i].devname);
+		base = "";
+		switch(IRQ_SRC(i)) {
+			case 1: base = "via1";
+				break;
+			case 2: if (oss_present) {
+					base = "oss";
+				} else {
+					base = "via2";
+				}
+				break;
+			case 3:
+			case 4:
+			case 5:
+			case 6: if (psc_present) {
+					base = "psc";
+				} else if (oss_present) {
+					base = "oss";
+				} else {
+					if (IRQ_SRC(i) == 4) base = "scc";
+				}
+				break;
+			case 7: base = "nbus";
+				break;
+		}
+		len += sprintf(buf+len, "%4s %2d: %10u ",
+				base, i, kstat.irqs[0][i]);
+
+		do {
+			if (node->flags & IRQ_FLG_FAST) {
+				len += sprintf(buf+len, "F ");
+			} else if (node->flags & IRQ_FLG_SLOW) {
+				len += sprintf(buf+len, "S ");
+			} else {
+				len += sprintf(buf+len, "  ");
+			}
+			len += sprintf(buf+len, "%s\n", node->devname);
+			if ((node = node->next)) {
+				len += sprintf(buf+len, "                    ");
+			}
+		} while(node);
 
 	}
-	len += sprintf(buf+len, "nubus spurious ints: %10lu\n",
-		       nubus_irqs[7]);
-	len += sprintf(buf+len, "nubus stuck events : %10lu\n",
-		       nubus_stuck_events);
-#ifdef CONFIG_BLK_DEV_IDE
-	len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n",
-		       mac_ide_irqs);
-#endif	
-
 	return len;
 }
 
-void via_scsi_clear(void)
-{
-	volatile unsigned char deep_magic;
-	if (via2_is_rbv) {
-		via_write(rbv_regp, rIFR, (1<<3)|(1<<0)|0x80);
-		deep_magic = via_read(rbv_regp, rBufB);
-	} else if (via2_is_oss) {
-		/* nothing */
-		/* via_write(oss_regp, 9, 0) */;
-	} else
-		deep_magic = via_read(via2_regp, vBufB);
-	mac_enable_irq( IRQ_IDX(IRQ_MAC_SCSI) );
-}
-
-
 void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs)
 {
 #ifdef DEBUG_SPURIOUS
-	if (console_loglevel > 6)
+	if (console_loglevel > 6) {
 		printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
+	}
 #endif
 }
 
@@ -854,9 +598,6 @@
 	}
 }
 
-void scsi_mac_debug(void);
-void scsi_mac_polled(void);
-
 static int in_nmi = 0;
 static volatile int nmi_hold = 0;
 
@@ -869,10 +610,6 @@
 	 */
 
 	in_nmi++;
-#if 0
-	scsi_mac_debug();
-	printk("PC: %08lx\nSR: %04x  SP: %p\n", fp->pc, fp->sr, fp);
-#endif
 	for (i=0; i<100; i++)
 		udelay(1000);
 
@@ -889,10 +626,6 @@
 	while (nmi_hold == 1)
 		udelay(1000);
 
-#if 0
-	scsi_mac_polled();
-#endif
-
 	if ( console_loglevel >= 8 ) {
 #if 0
 		show_state();
@@ -916,799 +649,12 @@
 }
 
 /*
- *	Unexpected via interrupt
- */
- 
-void via_wtf(int slot, void *via, struct pt_regs *regs)
-{
-#ifdef DEBUG_SPURIOUS
-	if (console_loglevel > 6)
-		printk("Unexpected nubus event %d on via %p\n",slot,via);
-#endif
-}
-
-/*
- * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
- * via6522.c :-), disable/pending masks added.
- * The int *viaidx etc. is just to keep the prototype happy ...
- */
-
-static void via_irq(unsigned char *via, int *viaidx, struct pt_regs *regs)
-{
-	unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F;
-	int i;
-	int ct = 0;
-	struct irqhandler *via_handler = handler_table[*viaidx];
-	struct irqparam   *via_param   = param_table[*viaidx];
-	unsigned long     *via_irqs    = mac_irqs[*viaidx];
-
-	/* to be changed, possibly: for each non'masked', enabled IRQ, read 
-	 * flag bit, ack and call handler ...
-         * Currently: all pending irqs ack'ed en bloc.
-	 * If ack for masked IRQ required: keep 'pending' info separate.
-	 */
-
-        /* shouldn't we disable interrupts here ?? */
-
-	
-	/*
-	 *	Shouldnt happen
-	 */
-	 
-	if(events==0)
-	{
-#ifdef DEBUG_VIA
-		/* should go away; mostly missing timer ticks and ADB events */
-		printk("via%d_irq: nothing pending, flags %x mask %x!\n",
-			*viaidx + 1, via_read(via, vIFR), via_read(via,vIER));
-#endif
-		via_irqs[7]++;
-		return;
-	}
-
-#ifdef DEBUG_VIA	
-	/*
-	 * limited verbosity for VIA interrupts
-	 */
-#if 0
-	if ( (*viaidx == 0 && events != 1<<6) 		/* timer int */
-	  || (*viaidx == 1 && events != 1<<3) )		/* SCSI IRQ */
-#else
-	if ( *viaidx == 0 && (events & 1<<2) ) 
-#endif
-		printk("via_irq: irq %d events %x !\n", (*viaidx)+1, events);
-#endif
-
-	do {
-		/*
-		 *	Clear the pending flag
-		 */
-		 
-		via_write(via, vIFR, events);
-		 
-		/*
-		 *	Now see what bits are raised
-		 */
-		 
-		for(i=0;i<7;i++)
-		{
-			/* determine machspec. irq no. */
-			int irq = ((*viaidx)+1)* 8 + i;
-		        /* call corresponding handlers */
-		        if (events&(1<<i)) {
-			        if (irq_flags[*viaidx].disabled & (1<<i)) {
-					if (!irq_flags[*viaidx].pending&(1<<i))
-						via_irqs[i]++;
-				        /* irq disabled -> mark pending */
-				        irq_flags[*viaidx].pending |= (1<<i);
-				} else {
-					via_irqs[i]++;
-				        /* irq enabled -> call handler */
-				        (via_handler[i].handler)(irq, via, regs);
-				}
-			}
-			/* and call handlers for pending irqs - first ?? */
-			if (    (irq_flags[*viaidx].pending  & (1<<i))
-			    && !(irq_flags[*viaidx].disabled & (1<<i)) ) {
-				/* call handler for re-enabled irq */
-			        (via_handler[i].handler)(irq, via, regs);
-				/* and clear pending flag :-) */
-				irq_flags[*viaidx].pending  &= ~(1<<i);
-			}
-		}
-	
-		/*
-		 *	And done ... check for more punishment!
-		 */
-
-		events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F;
-		ct++;
-		if(events && ct>8)
-		{
-#ifdef DEBUG_VIA
-		        printk("via%d: stuck events %x\n", (*viaidx)+1, events);
-#endif
-		        break;
-		}
-	}
-	while(events);
-#if 0
-	scsi_mac_polled();
-#endif
-}
-
-/*
- * Caution: the following stuff is called from process_int as _autovector_ 
- * system interrupts. So irq is always in the range 0-7 :-( and the selection
- * of the appropriate VIA is up to the irq handler here based on the autovec 
- * irq number. There's no information whatsoever about which source on the VIA
- * triggered the int - and that's what the machspec irq no's are about. 
- * Broken design :-((((
- */
-
-/*
- *	System interrupts
- */
-
-void via1_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-	int srcidx = IRQ_IDX(irq) - 1;
-	via_irq((unsigned char *)via1_regp, &srcidx, regs);
-}
-
-
-/*
- *	Nubus / SCSI interrupts, VIA style (could be wrapped into via1_irq or
- *	via_irq directly by selecting the regp based on the irq!)
- */
- 
-void via2_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-	int srcidx = IRQ_IDX(irq) - 1;
-	via_irq((unsigned char *)via2_regp, &srcidx, regs);
-}
-
-/*
- *	Nubus / SCSI interrupts; RBV style
- *	The RBV is different. RBV appears to stand for randomly broken
- *	VIA (or even real broken VIA).
- */
- 
-void rbv_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-	int srcidx = IRQ_IDX(irq) - 1;	/* MUST be 1 !! */
-	volatile unsigned char *via = rbv_regp;
-	unsigned char events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F;
-	int i;
-	int ct = 0;
-	struct irqhandler *via_handler = handler_table[srcidx];
-	struct irqparam   *via_param   = param_table[srcidx];
-
-        /* shouldn't we disable interrupts here ?? */
-
-	
-	/*
-	 *	Shouldnt happen
-	 */
-	 
-	if(events==0)
-	{
-#ifdef DEBUG_VIA
-		printk("rbv_irq: nothing pending, flags %x mask %x!\n",
-			via_read(via, rIFR), via_read(via,rIER));
-#endif
-		rbv_irqs[7]++;
-		return;
-	}
-
-#ifdef DEBUG_VIA	
-	/*
-	 * limited verbosity for RBV interrupts (add more if needed)
-	 */
-	if ( srcidx == 1 && events != 1<<3 )		/* SCSI IRQ */
-		printk("rbv_irq: irq %d (%d) events %x !\n", irq, srcidx+1, events);
-#endif
-
-	/* to be changed, possibly: for each non'masked', enabled IRQ, read 
-	 * flag bit, ack and call handler ...
-         * Currently: all pending irqs ack'ed en bloc.
-	 * If ack for masked IRQ required: keep 'pending' info separate.
-	 */
-
-	do {
-		/*
-		 *	Clear the pending flag
-		 */
-		 
-		via_write(via, rIFR, events | rbv_clear);
-		 
-		/*
-		 *	Now see what bits are raised
-		 */
-		 
-		for(i=0;i<7;i++)
-		{
-			/* determine machspec. irq no. */
-			int irq = (srcidx+1)* 8 + i;
-		        /* call corresponding handlers */
-		        if (events&(1<<i)) {
-			        if (irq_flags[srcidx].disabled & (1<<i)) {
-					if (!irq_flags[srcidx].pending&(1<<i))
-						rbv_irqs[i]++;
-				        /* irq disabled -> mark pending */
-				        irq_flags[srcidx].pending |= (1<<i);
-				} else {
-					rbv_irqs[i]++;
-				        /* irq enabled -> call handler */
-				        (via_handler[i].handler)(irq, via, regs);
-				}
-			}
-			/* and call handlers for pending irqs - first ?? */
-			if (    (irq_flags[srcidx].pending  & (1<<i))
-			    && !(irq_flags[srcidx].disabled & (1<<i)) ) {
-				/* call handler for re-enabled irq */
-			        (via_handler[i].handler)(irq, via, regs);
-				/* and clear pending flag :-) */
-				irq_flags[srcidx].pending  &= ~(1<<i);
-			}
-		}
-
-		/*
-		 *	And done ... check for more punishment!
-		 */
-
-		events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F;
-		ct++;
-		if(events && ct>8)
-		{
-		        printk("rbv: stuck events %x\n",events);
-			for(i=0;i<7;i++)
-			{
-				if(events&(1<<i))
-				{
-					printk("rbv - bashing source %d\n",
-						i);
-					via_write(via, rIER, 1<<i);
-					via_write(via, rIFR, (1<<i) | rbv_clear);
-				}
-			}
-		        break;
-		}
-	}
-	while(events);
-#if 0
-	scsi_mac_polled();
-#endif
-}
-
-/*
- *	Nubus / SCSI interrupts; OSS style
- *	The OSS is even more different than the RBV. OSS appears to stand for 
- *	Obscenely Screwed Silicon ... 
- *
- *	Latest NetBSD sources suggest the OSS should behave like a RBV, but 
- *	that's probably true for the 0x203 offset (Nubus/ADB-SWIM IOP) at best
- */
- 
-void oss_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-	int srcidx = IRQ_IDX(irq) - 1;	/* MUST be 1 !! */
-	volatile unsigned char *via = oss_regp;
-	unsigned char events=(via_read(via, oIFR))&0x03;
-	unsigned char nub_ev=(via_read(via, nIFR))&0x4F;
-	unsigned char adb_ev;
-	int i;
-	int ct = 0;
-	struct irqhandler *via_handler = handler_table[srcidx];
-	struct irqparam   *via_param   = param_table[srcidx];
-
-        /* shouldn't we disable interrupts here ?? */
-
-	adb_ev = nub_ev & 0x40;
-	nub_ev &= 0x3F;
-
-	/*
-	 *	Shouldnt happen
-	 */
-
-	if (events==0 && adb_ev==0 && nub_ev==0)
-	{
-		printk("oss_irq: nothing pending, flags %x %x!\n",
-			via_read(via, oIFR), via_read(via, nIFR));
-		rbv_irqs[7]++;
-		return;
-	}
-
-#ifdef DEBUG_VIA	
-	/*
-	 * limited verbosity for RBV interrupts (add more if needed)
-	 */
-	if ( events != 1<<3 )		/* SCSI IRQ */
-		printk("oss_irq: irq %d srcidx+1 %d events %x %x %x !\n", irq, srcidx+1, 
-			events, adb_ev, nub_ev);
-#endif
-
-	/* 
-	 * OSS priorities: call ADB handler first if registered, other events,
-	 * then Nubus 
-	 * ADB: yet to be implemented!
-	 */
-
-	/*
-	 * ADB: try to shutup the IOP 
-	 */
-	if (adb_ev) {
-		printk("Hands off ! Don't press this button ever again !!!\n");
-		via_write(via, 6, 0);
-	}
-
-	do {
-		/*
-		 *	Clear the pending flags
-		 *	How exactly is that supposed to work ??
-		 */
-
-		/*
-		 *	Now see what bits are raised
-		 */
-		 
-		for(i=0;i<7;i++)
-		{
-			/* HACK HACK: map to bit number in OSS register */
-			int irqidx = oss_map[i];
-			/* determine machspec. irq no. */
-			int irq = (srcidx+1)* 8 + i;
-		        /* call corresponding handlers */
-		        if ( (events&(1<<irqidx)) && 		/* bit set*/
-		             (via_read(via, irqidx+8)&0x7) ) {	/* irq enabled */
-			        if (irq_flags[srcidx].disabled & (1<<i)) {
-					if (!irq_flags[srcidx].pending&(1<<i))
-						rbv_irqs[i]++;
-				        /* irq disabled -> mark pending */
-				        irq_flags[srcidx].pending |= (1<<i);
-				} else {
-					rbv_irqs[i]++;
-				        /* irq enabled -> call handler */
-				        (via_handler[i].handler)(irq, via, regs);
-				}
-			}
-			/* and call handlers for pending irqs - first ?? */
-			if (    (irq_flags[srcidx].pending  & (1<<i))
-			    && !(irq_flags[srcidx].disabled & (1<<i)) ) {
-				/* call handler for re-enabled irq */
-			        (via_handler[i].handler)(irq, via, regs);
-				/* and clear pending flag :-) */
-				irq_flags[srcidx].pending  &= ~(1<<i);
-			}
-		}
-	
-		/*
-		 *	And done ... check for more punishment!
-		 */
-
-		events=(via_read(via, oIFR)/*&via_read(via,rIER)*/)&0x03;
-		ct++;
-		if(events && ct>8)
-		{
-		        printk("oss: stuck events %x\n",events);
-			for(i=0;i<7;i++)
-			{
-				if(events&(1<<i))
-				{
-					printk("oss - bashing source %d\n",
-						i);
-					/* that should disable it */
-					via_write(via, 8+i, 0);
-				}
-			}
-		        break;
-		}
-	}
-	while(events);
-#if 0
-	scsi_mac_polled();
-#endif
-
-	if (nub_ev)
-		oss_do_nubus(irq, via, regs);
-
-}
-
-/*
- *	Unexpected slot interrupt
- */
- 
-void nubus_wtf(int slot, void *via, struct pt_regs *regs)
-{
-#ifdef DEBUG_NUBUS_SPURIOUS
-	if (console_loglevel > 6)
-		printk("Unexpected interrupt on nubus slot %d\n",slot);
-#endif
-}
-
-/*
  *	SCC master interrupt handler; sole purpose: pass the registered 
  *	async struct to the SCC handler proper.
  */
 
 void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs)
 {
-	int i;
-				/* 1+2: compatibility with PSC ! */
-	for (i = 1; i < 3; i++) /* currently only these two used */
-		if (scc_handler[i].handler != mac_default_handler) {
-			(scc_handler[i].handler)(i, scc_handler[i].dev_id, regs);
-			scc_irqs[i]++;
-		}
-}
-
-/*
- *	PSC interrupt handler
- */
-
-void psc_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-	int srcidx = IRQ_IDX(irq) - 1;
-	volatile unsigned char *via = psc_regp;
-	unsigned int pIFR = 0x100 + 0x10*srcidx;
-	unsigned int pIER = 0x104 + 0x10*srcidx;
-	unsigned char events=(via_read(via, pIFR)&via_read(via,pIER))&0xF;
-	int i;
-	int ct = 0;
-	struct irqhandler *via_handler = handler_table[srcidx];
-	struct irqparam   *via_param   = param_table[srcidx];
-
-        /* shouldn't we disable interrupts here ?? */
-
-	
-	/*
-	 *	Shouldnt happen
-	 */
-	 
-	if(events==0)
-	{
-#ifdef DEBUG_VIA
-		printk("psc_irq: nothing pending, flags %x mask %x!\n",
-			via_read(via, pIFR), via_read(via,pIER));
-#endif
-		mac_irqs[srcidx][7]++;
-		return;
-	}
-
-#ifdef DEBUG_VIA	
-	/*
-	 * limited verbosity for PSC interrupts (add more if needed)
-	 */
-	if ( srcidx == 1 && events != 1<<3 && events != 1<<1 )		/* SCSI IRQ */
-		printk("psc_irq: irq %d srcidx+1 %d events %x !\n", irq, srcidx+1, events);
-#endif
-
-	/* to be changed, possibly: for each non'masked', enabled IRQ, read 
-	 * flag bit, ack and call handler ...
-         * Currently: all pending irqs ack'ed en bloc.
-	 * If ack for masked IRQ required: keep 'pending' info separate.
-	 */
-
-	do {
-		/*
-		 *	Clear the pending flag
-		 */
-		 
-		/* via_write(via, pIFR, events); */
-		 
-		/*
-		 *	Now see what bits are raised
-		 */
-		 
-		for(i=0;i<7;i++)
-		{
-			/* determine machspec. irq no. */
-			int irq = (srcidx+1)* 8 + i;
-		        /* call corresponding handlers */
-		        if (events&(1<<i)) {
-			        if (irq_flags[srcidx].disabled & (1<<i)) {
-					if (!irq_flags[srcidx].pending&(1<<i))
-						mac_irqs[srcidx][i]++;
-				        /* irq disabled -> mark pending */
-				        irq_flags[srcidx].pending |= (1<<i);
-				} else {
-					mac_irqs[srcidx][i]++;
-				        /* irq enabled -> call handler */
-				        (via_handler[i].handler)(irq, via, regs);
-				}
-			}
-			/* and call handlers for pending irqs - first ?? */
-			if (    (irq_flags[srcidx].pending  & (1<<i))
-			    && !(irq_flags[srcidx].disabled & (1<<i)) ) {
-				/* call handler for re-enabled irq */
-			        (via_handler[i].handler)(irq, via, regs);
-				/* and clear pending flag :-) */
-				irq_flags[srcidx].pending  &= ~(1<<i);
-			}
-		}
-
-		/*
-		 *	And done ... check for more punishment!
-		 */
-
-		events=(via_read(via,pIFR)&via_read(via,pIER))&0x7F;
-		ct++;
-		if(events && ct>8)
-		{
-		        printk("psc: stuck events %x\n",events);
-			for(i=0;i<7;i++)
-			{
-				if(events&(1<<i))
-				{
-					printk("psc - bashing source %d\n",
-						i);
-					via_write(via, pIER, 1<<i);
-					/* via_write(via, pIFR, (1<<i)); */
-				}
-			}
-		        break;
-		}
-	}
-	while(events);
-}
-
-
-/*
- *	Nubus handling
- *      Caution: slot numbers are currently 'hardcoded' to the range 9-15!
- *      In general, the same request_irq() functions as above can be used if
- *      the interrupt numbers specifed in macints.h are used.
- */
- 
-static int nubus_active=0;
- 
-int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *))
-{
-	slot-=9;
-/*	printk("Nubus request irq for slot %d\n",slot);*/
-	if(nubus_handler[slot].handler!=nubus_wtf)
-		return -EBUSY;
-	nubus_handler[slot].handler=handler;
-	nubus_handler[slot].dev_id =dev_id;
-	nubus_param[slot].flags    = IRQ_FLG_LOCK;
-	nubus_param[slot].devname  = "nubus slot";
-
-	/* 
-	 * if no nubus int. was active previously: register the main nubus irq
-	 * handler now!
-	 */
-
-	if (!nubus_active && !via2_is_oss) {
-		request_irq(IRQ_MAC_NUBUS, via_do_nubus, IRQ_FLG_LOCK, 
-			    "nubus dispatch", via_do_nubus);
-		mac_turnon_irq(IRQ_MAC_NUBUS);
-	}
-
-	nubus_active|=1<<slot;
-/*	printk("program slot %d\n",slot);*/
-/*	printk("via2=%p\n",via2);*/
-#if 0
-	via_write(via2, vDirA, 
-		via_read(via2, vDirA)|(1<<slot));
-	via_write(via2, vBufA, 0);
-#endif		
-	if (via2_is_oss)
-		via_write(oss_regp, slot, 2);
-	else if (!via2_is_rbv) {
-		/* Make sure the bit is an input */
-		via_write(via2_regp, vDirA, 
-			via_read(via2_regp, vDirA)&~(1<<slot));
-	}
-/*	printk("nubus irq on\n");*/
-	return 0;
-}
-
-int nubus_free_irq(int slot)
-{
-	slot-=9;
-	nubus_active&=~(1<<slot);
-	nubus_handler[slot].handler=nubus_wtf;
-	nubus_handler[slot].dev_id = NULL;
-	nubus_param[slot].flags    = IRQ_FLG_STD;
-	nubus_param[slot].devname  = NULL;
-
-	if (via2_is_rbv)
-		via_write(rbv_regp, rBufA, 1<<slot);
-	else if (via2_is_oss)
-		via_write(oss_regp, slot, 0);
-	else {
-		via_write(via2_regp, vDirA, 
-			via_read(via2_regp, vDirA)|(1<<slot));
-		via_write(via2_regp, vBufA, 1<<slot);
-		via_write(via2_regp, vDirA, 
-			via_read(via2_regp, vDirA)&~(1<<slot));
-	}
-	return 0;
-}
-
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-/*
- * IDE interrupt hook
- */
-extern void (*mac_ide_intr_hook)(int, void *, struct pt_regs *);
-extern int (*mac_ide_irq_p_hook)(void);
-#endif
-
-/*
- * Nubus dispatch handler - VIA/RBV style
- */
-static void via_do_nubus(int slot, void *via, struct pt_regs *regs)
-{
-	unsigned char map, allints;
-	int i;
-	int ct=0;
-	int ide_pending = 0;
-		
-	/* lock the nubus interrupt */
-	/* That's just 'clear Nubus IRQ bit in VIA2' BTW. Pretty obsolete ? */
-	if (via2_is_rbv) 
-		via_write(rbv_regp, rIFR, 0x82);
-	else
-		via_write(via2_regp, vIFR, 0x82);
-	
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-	/* IDE hack */
-	if (mac_ide_intr_hook) {
-		/* 'slot' is lacking the machspec bit in 2.0 */
-		/* need to pass proper dev_id = hwgroup here */
-		mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs);
-		mac_ide_irqs++;
-	}
-#endif
-
-	while(1)
-	{
-		if (via2_is_rbv)
-			allints = ~via_read(rbv_regp, rBufA);
-		else
-			allints = ~via_read(via2_regp, vBufA);
-		
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-		if (mac_ide_irq_p_hook)
-			ide_pending = mac_ide_irq_p_hook();
-#endif
-
-		if ( (map = (allints&nubus_active)) == 0 
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-		      && !ide_pending 
-#endif
-							) 
-		{
-			if (ct == 0) {
-#ifdef DEBUG_VIA_NUBUS
-				if (console_loglevel > 5)
-					printk("nubus_irq: nothing pending, map %x mask %x active %x\n", 
-						allints, nubus_active, map);
-#endif
-				nubus_irqs[7]++;
-			}
-			/* clear it */
-			if (allints)
-				if (via2_is_rbv)
-					via_write(rbv_regp, rIFR, 0x02);
-				else
-					via_write(via2_regp, vIFR, 0x02);
-			break;
-		}
-
-#ifdef DEBUG_VIA_NUBUS
-		if (console_loglevel > 6)
-			printk("nubus_irq: map %x mask %x active %x\n", 
-				allints, nubus_active, map);
-#endif
-
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-		if (mac_ide_intr_hook && ide_pending) {
-			mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs);
-			mac_ide_irqs++;
-		}
-#endif
-
-		if(ct++>2)
-		{
-			if (console_loglevel > 5)
-				printk("nubus stuck events - %x/%x/%x ide %x\n", 
-				allints, nubus_active, map, ide_pending);
-			nubus_stuck_events++;
-
-			return;
-		}
-
-		for(i=0;i<7;i++)
-		{
-			if(map&(1<<i))
-			{
-				nubus_irqs[i]++;
-				(nubus_handler[i].handler)(i+9, nubus_handler[i].dev_id, regs);
-			}
-		}
-		/* clear it */
-		if (via2_is_rbv)
-			via_write(rbv_regp, rIFR, 0x02);
-		else
-			via_write(via2_regp, vIFR, 0x02);
-
-	}
-	
-	/* And done */
-}
-
-/*
- * Nubus dispatch handler - OSS style
- */
-static void oss_do_nubus(int slot, void *via, struct pt_regs *regs)
-{
-	unsigned char map;
-	int i;
-	int ct=0;
-
-/*	printk("nubus interrupt\n");*/
-		
-#if 0
-	/* lock the nubus interrupt */
-	if (via2_is_rbv) 
-		via_write(rbv_regp, rIFR, 0x82);
-	else
-		via_write(via2_regp, vIFR, 0x82);
-#endif
-
-	/* IDE hack for Quadra: uses Nubus interrupt without any slot bit set */
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-	if (mac_ide_intr_hook)
-		mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs);
-#endif
-	
-	while(1)
-	{
-		/* pending events */
-		map=(via_read(via, nIFR))&0x3F;
-		
-#ifdef DEBUG_VIA_NUBUS
-		printk("nubus_irq: map %x mask %x\n", map, nubus_active);
-#endif
-		if( (map = (map&nubus_active)) ==0 ) {
-			if (ct == 0) {
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-				if (!mac_ide_intr_hook)
-					printk("nubus_irq: nothing pending, map %x mask %x\n", 
-						map, nubus_active);
-#endif
-				nubus_irqs[7]++;
-			}
-			break;
-		}
-
-		if(ct++>2)
-		{
-#if 0
-			printk("nubus stuck events - %d/%d\n", map, nubus_active);
-#endif
-			return;
-		}
-
-		for(i=0;i<7;i++)
-		{
-			if(map&(1<<i))
-			{
-				nubus_irqs[i]++;
-				/* call handler */
-				(nubus_handler[i].handler)((i+9), nubus_handler[i].dev_id, regs);
-				/* clear interrupt ?? */
-#if 0
-				via_write(oss_regp, i, 0);
-#endif
-			}
-		}
-		/* clear it */ 
-#if 0
-		via_write(oss_regp, nIFR, map);
-#endif
-	}
-	
-	/* And done */
+	mac_do_irq_list(IRQ_SCCA, regs);
+	mac_do_irq_list(IRQ_SCCB, regs);
 }
diff -urN linux-2.2.18-pre15/arch/m68k/mac/mackeyb.c linux/arch/m68k/mac/mackeyb.c
--- linux-2.2.18-pre15/arch/m68k/mac/mackeyb.c	Mon Apr 26 13:38:03 1999
+++ linux/arch/m68k/mac/mackeyb.c	Mon Dec  3 20:33:54 2001
@@ -71,9 +71,10 @@
 #ifdef CONFIG_ADBMOUSE
 /* XXX: Hook for mouse driver */
 void (*adb_mouse_interrupt_hook)(unsigned char *, int);
-int adb_emulate_buttons = 0;
-int adb_button2_keycode = 0x7d;	/* right control key */
-int adb_button3_keycode = 0x7c; /* right option key */
+/* Turn this on by default */
+int adb_emulate_buttons = 1;
+int adb_button2_keycode = 0x7c; /* right option key */
+int adb_button3_keycode = 0x7d;	/* right control key */
 #endif
 
 /* The mouse driver - for debugging */
@@ -111,7 +112,7 @@
 	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf109,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 u_short mac_shift_map[NR_KEYS] __initdata = {
@@ -130,7 +131,7 @@
 	0xf10e,	0xf10f,	0xf110,	0xf10c,	0xf111,	0xf112,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf203,	0xf200,	0xf113,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf20b,	0xf116,	0xf10d,	0xf117,
-	0xf10b,	0xf20a,	0xf10a,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf10b,	0xf20a,	0xf10a,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 u_short mac_altgr_map[NR_KEYS] __initdata = {
@@ -149,7 +150,7 @@
 	0xf510,	0xf511,	0xf512,	0xf50e,	0xf513,	0xf514,	0xf200,	0xf516,
 	0xf200,	0xf10c,	0xf200,	0xf202,	0xf200,	0xf515,	0xf200,	0xf517,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf50f,	0xf117,
-	0xf50d,	0xf119,	0xf50c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf50d,	0xf119,	0xf50c,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 u_short mac_ctrl_map[NR_KEYS] __initdata = {
@@ -168,7 +169,7 @@
 	0xf104,	0xf105,	0xf106,	0xf102,	0xf107,	0xf108,	0xf200,	0xf10a,
 	0xf200,	0xf10c,	0xf200,	0xf204,	0xf200,	0xf109,	0xf200,	0xf10b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf103,	0xf117,
-	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf101,	0xf119,	0xf100,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 u_short mac_shift_ctrl_map[NR_KEYS] __initdata = {
@@ -187,7 +188,7 @@
 	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf200,	0xf117,
-	0xf200,	0xf119,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf20c,
+	0xf200,	0xf119,	0xf200,	0xf700,	0xf701,	0xf702,	0xf200,	0xf20c,
 };
 
 u_short mac_alt_map[NR_KEYS] __initdata = {
@@ -206,7 +207,7 @@
 	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
 	0xf200,	0xf10c,	0xf200,	0xf209,	0xf200,	0xf509,	0xf200,	0xf50b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 u_short mac_ctrl_alt_map[NR_KEYS] __initdata = {
@@ -225,7 +226,7 @@
 	0xf504,	0xf505,	0xf506,	0xf502,	0xf507,	0xf508,	0xf200,	0xf50a,
 	0xf200,	0xf10c,	0xf200,	0xf200,	0xf200,	0xf509,	0xf200,	0xf50b,
 	0xf200,	0xf11d,	0xf115,	0xf114,	0xf118,	0xf116,	0xf503,	0xf117,
-	0xf501,	0xf119,	0xf500,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf501,	0xf119,	0xf500,	0xf700,	0xf701,	0xf702,	0xf200,	0xf200,
 };
 
 extern unsigned int keymap_count;
@@ -239,19 +240,19 @@
 static struct adb_request led_request;
 extern int in_keybinit;
 
+
 /*
  * machdep keyboard routines, interface and key repeat method modeled after
  * drivers/macintosh/keyb_mac.c
  */
 
-int mac_kbd_translate(unsigned char keycode, unsigned char *keycodep,
-		     char raw_mode)
+int mackbd_translate(unsigned char keycode, unsigned char *keycodep,
+		      char raw_mode)
 {
 	if (!raw_mode) {
 		/*
 		 * Convert R-shift/control/option to L version.
 		 * Remap keycode 0 (A) to the unused keycode 0x5a.
-		 * Other parts of the system assume 0 is not a valid keycode.
 		 */
 		switch (keycode) {
 		case 0x7b: keycode = 0x38; break; /* R-shift */
@@ -260,11 +261,12 @@
 		case 0:	   keycode = 0x5a; break; /* A */
 		}
 	}
+
 	*keycodep = keycode;
 	return 1;
 }
 
-int mac_kbd_unexpected_up(unsigned char keycode)
+int mackbd_unexpected_up(unsigned char keycode)
 {
 	return 0x80;
 }
@@ -297,70 +299,41 @@
 #ifdef CONFIG_ADBMOUSE
 	/*
 	 * XXX: Add mouse button 2+3 fake codes here if mouse open.
-	 *	As we only report up/down events, keep track of faked buttons.
+	 *	Keep track of 'button' states here as we only send 
+	 *	single up/down events!
 	 *	Really messy; might need to check if keyboard is in
-	 *	VC_RAW mode for X?.
+	 *	VC_RAW mode.
 	 *	Might also want to know how many buttons need to be emulated.
 	 *	-> hide this as function in arch/m68k/mac ?
-	 *	Current emulation buttons: right alt/option and control
-	 *	(wanted: command and alt/option, or KP= and KP( ...)
-	 *	Debug version; might be rewritten to be faster on normal keys.
 	 */
-	if (adb_emulate_buttons 
-	    && (adb_mouse_interrupt_hook || console_loglevel >= 8)) {
-		unsigned char button, button2, button3, fake_event;
-		static unsigned char button2state=0, button3state=0; /* up */
+	if (adb_emulate_buttons
+	    && (keycode == adb_button2_keycode
+		|| keycode == adb_button3_keycode)
+	    && (adb_mouse_interrupt_hook || console_loglevel == 10)) {
+		int button;
 		/* faked ADB packet */
 		static unsigned char data[4] = { 0, 0x80, 0x80, 0x80 };
 
-		button = 0;
-		fake_event = 0;
-		if (keycode == adb_button2_keycode) {	/* which 'button' ? */
-			/* R-option */
-			button2 = (!up_flag);		/* new state */
-			if (button2 != button2state)	/* change ? */
-				button = 2; 
-			button2state = button2;		/* save state */
-			fake_event = 2;
-		} else if (keycode == adb_button3_keycode) {
-			/* R-control */
-			button3 = (!up_flag);		/* new state */
-			if (button3 != button3state)	/* change ? */ 
-				button = 3; 
-			button3state = button3; 	/* save state */
-			fake_event = 3;
-		}
-#ifdef DEBUG_ADBMOUSE
-		if (fake_event && console_loglevel >= 8)
-			printk("fake event: button2 %d button3 %d button %d\n",
-				 button2state, button3state, button);
-#endif
-		if (button) {		/* there's been a button state change */
-			/* fake a mouse packet : send all bytes, change one! */
-			data[button] = (up_flag ? 0x80 : 0);
+		button = keycode == adb_button2_keycode? 2: 3;
+		if (data[button] != up_flag) {
+			/* send a fake mouse packet */
+			data[button] = up_flag;
+			if (console_loglevel >= 8)
+				printk("fake mouse event: %x %x %x\n",
+				       data[1], data[2], data[3]);
 			if (adb_mouse_interrupt_hook)
 				adb_mouse_interrupt_hook(data, 4);
-#ifdef DEBUG_ADBMOUSE
-			else
-				printk("mouse_fake: data %2x %2x %2x buttons %2x \n", 
-					data[1], data[2], data[3],
-					~( (data[1] & 0x80 ? 0 : 4) 
-					 | (data[2] & 0x80 ? 0 : 1) 
-					 | (data[3] & 0x80 ? 0 : 2) )&7 );
-#endif
 		}
-		/*
-		 * for mouse 3-button emulation: don't process 'fake' keys!
-		 * Keys might autorepeat, and console state gets generally messed
-		 * up enough so that selection stops working.
-		 */
-		if (fake_event)
-			return;
+		return;
 	}
 #endif /* CONFIG_ADBMOUSE */
 
 	/*
-	 * Convert R-shift/control/option to L version.
+	 * This should not be done in raw mode, but basically X is
+	 * all screwed up, so we try to make it less so by adjusting
+	 * things.  Note that this is also redundant with
+	 * mackbd_translate() above.  Either we are wrong somewhere
+	 * or X is wrong, and I'm betting on the latter.
 	 */
 	switch (keycode) {
 		case 0x7b: keycode = 0x38; break; /* R-shift */
@@ -493,16 +466,18 @@
 static void
 mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
 {
-	struct kbd_struct *kbd;
-	int i;
+#ifdef DEBUG_ADB
+	if (console_loglevel == 10 &&
+	    (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG)) {
+		int i;
 
-	if (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG) {
-		printk("data from mouse:");
+		printk(KERN_DEBUG "data from mouse:");
 		for (i = 0; i < nb; ++i)
 			printk(" %x", data[i]);
 		printk("\n");
 		return;
 	}
+#endif
 
 	if (adb_mouse_interrupt_hook) {
 		adb_mouse_interrupt_hook(data+2, nb-2);
@@ -512,60 +487,6 @@
 		 */
 		return;
 	} 
-#ifdef DEBUG_ADBMOUSE
-	else
-		if (console_loglevel >= 8)
-		    printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n", 
-			data[3], data[4], data[5],
-			~((data[3] & 0x80 ? 0 : 4) 
-			| (data[4] & 0x80 ? 0 : 1)
-			| (data[5] & 0x80 ? 0 : 2))&7,
-			((data[4]&0x7f) < 64 ? (data[4]&0x7f) : (data[4]&0x7f)-128 ),
-			((data[3]&0x7f) < 64 ? -(data[3]&0x7f) : 128-(data[3]&0x7f) ) );
-#endif
-
-
-	kbd = kbd_table + fg_console;
-
-#if 0	/* The entirely insane way of MkLinux handling mouse input */
-	/* Requires put_queue which is static in keyboard.c :-( */
-	/* Only send mouse codes when keyboard is in raw mode. */
-	if (kbd->kbdmode == VC_RAW) {
-		static unsigned char uch_ButtonStateSecond = 0;
-		unsigned char uchButtonSecond;
-
-		/* Send first button, second button and movement. */
-		put_queue( 0x7e );
-		put_queue( data[3] );
-		put_queue( data[4] );
-
-		/* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */
-
-		/* Store the button state. */
-		uchButtonSecond = (data[4] & 0x80);
-
-		/* Send second button. */
-		if (uchButtonSecond != uch_ButtonStateSecond) {
-			put_queue( 0x3f | uchButtonSecond );
-			uch_ButtonStateSecond = uchButtonSecond;
-		}
-
-		/* Macintosh 3-button mouse (handler 4). */
-		if ((nb == 6) && (data[1] & 0x40)) {
-			static unsigned char uch_ButtonStateThird = 0;
-			unsigned char uchButtonThird;
-
-			/* Store the button state for speed. */
-			uchButtonThird = (data[5] & 0x80);
-
-			/* Send third button. */
-			if (uchButtonThird != uch_ButtonStateThird) {
-				put_queue( 0x40 | uchButtonThird );
-				uch_ButtonStateThird = uchButtonThird;
-			}
-		}
-	}
-#endif	/* insane MkLinux mouse hack */
 }
 
 /* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
@@ -582,13 +503,11 @@
 
 static int leds_pending;
 
-void mac_kbd_leds(unsigned int leds)
+void mackbd_leds(unsigned int leds)
 {
-	if (led_request.got_reply) {
-#ifdef DEBUG_ADB
+	if (led_request.complete) {
 		if (console_loglevel == 10)
-			printk("mac_kbd_leds: got reply, sending request!\n");
-#endif
+			printk(KERN_DEBUG "mackbd_leds: got reply, sending request!\n");
 		adb_request(&led_request, mac_leds_done, 4, ADB_PACKET,
 			     ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
 			     0xff, ~mac_ledmap[leds]);
@@ -603,20 +522,15 @@
 	if (leds_pending) {
 		leds = leds_pending & 0xff;
 		leds_pending = 0;
-		mac_kbd_leds(leds);
+		mackbd_leds(leds);
 	}
 	mark_bh(KEYBOARD_BH);
 }
 
-int mac_kbdrate(struct kbd_repeat *k)
+__initfunc(int mackbd_init_hw(void))
 {
-	return 0;
-}
-
-__initfunc(int mac_keyb_init(void))
-{
-	static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req;
-	volatile int ct;
+	static struct adb_request autopoll_req, confcod_req;
+	int ct;
 
 	/* setup key map */
 	memcpy(key_maps[0], mac_plain_map, sizeof(plain_map));
@@ -629,12 +543,7 @@
 
 	/* initialize mouse interrupt hook */
 	adb_mouse_interrupt_hook = NULL;
-
-	/*
-	 * Might put that someplace else, possibly ....
-	 */
-	adb_bus_init();
-
+	
 	/* the input functions ... */	
 	adb_register(ADB_KEYBOARD, keyboard_input);
 	adb_register(ADB_MOUSE, mouse_input);
@@ -653,17 +562,13 @@
 	 */
 
 	if (macintosh_config->adb_type == MAC_ADB_CUDA) {
-		printk("CUDA autopoll on ...\n");
+		printk(KERN_DEBUG "CUDA autopoll on ...\n");
 		adb_request(&autopoll_req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
-		ct=0; 
-		while (!autopoll_req.got_reply && ++ct<1000)
-		{
-				udelay(10);
-		}
-		if(ct==1000) {
-			printk("Keyboard timed out.\n");
-			autopoll_req.got_reply = 1;
-		}
+		ct = 0;
+		while (!autopoll_req.complete && ct++ < 1000)
+			adb_poll();
+		if (ct == 1000) printk(KERN_ERR "ADB timeout\n");
+		autopoll_req.complete = 1;
 	}
 
 	/*
@@ -671,15 +576,15 @@
 	 *	care of that for other Macs.
 	 */
 
-	printk("Configuring keyboard:\n");
-
-	udelay(3000);
+	printk(KERN_DEBUG "Configuring keyboard:\n");
+	/* Aarrrgggh!  Die in hell! */
+	udelay(8000);
 
 	/* 
 	 * turn on all leds - the keyboard driver will turn them back off 
-	 * via mac_kbd_leds if everything works ok!
+	 * via mackbd_leds if everything works ok!
 	 */
-	printk("leds on ...\n");
+	printk(KERN_DEBUG "leds on ...\n");
 	adb_request(&led_request, NULL, 4, ADB_PACKET,
 		     ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~7);
 
@@ -687,19 +592,13 @@
 	 * The polling stuff should go away as soon as the ADB driver is stable
 	 */
 	ct = 0;
-	while (!led_request.got_reply && ++ct<1000)
-	{
-		udelay(10);
-	}
-	if(ct==1000) {
-		printk("keyboard timed out.\n");
-		led_request.got_reply  = 1;
-	}
+	while (!led_request.complete && ct++ < 1000)
+		adb_poll();
+	if (ct == 1000) printk(KERN_ERR "ADB timeout\n");
+	led_request.complete = 1;
 
-#if 1
-	printk("configuring coding mode ...\n");
-
-	udelay(3000);
+	printk(KERN_DEBUG "configuring coding mode ...\n");
+	udelay(8000);
 
 	/* 
 	 * get the keyboard to send separate codes for
@@ -707,56 +606,15 @@
 	 */
 	adb_request(&confcod_req, NULL, 4, ADB_PACKET, 
 		     ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
-
-	ct=0; 
-	while (!confcod_req.got_reply && ++ct<1000)
-	{
-		udelay(10);
-	}
-	if(ct==1000) {
-		printk("keyboard timed out.\n");
-		confcod_req.got_reply  = 1;
-	}
-#endif
-
-#if 0	/* seems to hurt, at least Geert's Mac */
-	printk("Configuring mouse (3-button mode) ...\n");
-
-	udelay(3000);
-
-	/* 
-	 * XXX: taken from the PPC driver again ... 
-	 * Try to switch the mouse (id 3) to handler 4, for three-button
-	 * mode. (0x20 is Service Request Enable, 0x03 is Device ID). 
-	 */
-	adb_request(&mouse_req, NULL, 4, ADB_PACKET,
-		    ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
-
-	ct=0; 
-	while (!mouse_req.got_reply && ++ct<1000)
-	{
-		udelay(10);
-	}
-	if(ct==1000)
-		printk("Mouse timed out.\n");
-#endif
-
-#if 0
-	printk("Start polling keyboard ...\n");
-
-	/* 
-	 *	get the keyboard to send data back, via the adb_input hook
-	 *	XXX: was never used properly, and the driver takes care
-	 *	of polling and timeout retransmits now.
-	 *	Might be of use if we want to start talking to a specific
-	 *	device here...
-	 */
-	adb_request(&readkey_req, NULL, 2, ADB_PACKET, 
-		     ADB_READREG(ADB_KEYBOARD, KEYB_KEYREG));
-#endif
+	ct = 0;
+	while (!confcod_req.complete && ct++ < 1000)
+		adb_poll();
+	if (ct == 1000) printk(KERN_ERR "ADB timeout\n");
+	confcod_req.complete = 1;
 
 	in_keybinit = 0;
-	printk("keyboard init done\n");
+	printk(KERN_DEBUG "keyboard init done\n");
+	udelay(8000);
 
 	return 0;
 }
diff -urN linux-2.2.18-pre15/arch/m68k/mac/oss.c linux/arch/m68k/mac/oss.c
--- linux-2.2.18-pre15/arch/m68k/mac/oss.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/oss.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,314 @@
+/*
+ *	OSS handling
+ *	Written by Joshua M. Thompson (funaho@jurai.org)
+ *
+ *
+ *	This chip is used in the IIfx in place of VIA #2. It acts like a fancy
+ *	VIA chip with prorammable interrupt levels.
+ *
+ * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
+ *		  recent insights into OSS operational details.
+ * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped
+ *		  to mostly match the A/UX interrupt scheme supported on the
+ *		  VIA side. Also added support for enabling the ISM irq again
+ *		  since we now have a functional IOP manager.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/bootinfo.h> 
+#include <asm/machw.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+int oss_present;
+volatile struct mac_oss *oss;
+
+void oss_irq(int, void *, struct pt_regs *);
+void oss_nubus_irq(int, void *, struct pt_regs *);
+
+extern void via1_irq(int, void *, struct pt_regs *);
+extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern int console_loglevel;
+
+/*
+ * Initialize the OSS
+ *
+ * The OSS "detection" code is actually in via_init() which is always called
+ * before us. Thus we can count on oss_present being valid on entry.
+ */
+
+__initfunc(void oss_init(void))
+{
+	int i;
+
+	if (!oss_present) return;
+
+	oss = (struct mac_oss *) OSS_BASE;
+
+	/* Disable all interrupts. Unlike a VIA it looks like we    */
+	/* do this by setting the source's interrupt level to zero. */
+
+	for (i = 0; i <= OSS_NUM_SOURCES; i++) {
+		oss->irq_level[i] = OSS_IRQLEV_DISABLED;
+	}
+	/* If we disable VIA1 here, we never really handle it... */
+	oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
+}
+
+/*
+ * Register the OSS and NuBus interrupt dispatchers.
+ */
+
+__initfunc(void oss_register_interrupts(void))
+{
+	sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
+			"OSS SCSI Dispatch", (void *) oss);
+	sys_request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK,
+			"SCC Dispatch", mac_SCC_handler);
+	sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
+			"Nubus Dispatch", (void *) oss);
+	sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
+			"OSS Sound Dispatch", (void *) oss);
+	sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
+			"VIA1 Dispatch", (void *) via1);
+}
+
+/*
+ * Initialize OSS for Nubus access
+ */
+
+__initfunc(void oss_nubus_init(void))
+{
+}
+
+/*
+ * Turn off the power via the ROM control register
+ *
+ * FIXME: not sure how this is supposed to work exactly...
+ */
+
+void oss_poweroff(void)
+{
+	oss->rom_ctrl = OSS_POWEROFF;
+
+	/* We should never make it this far... */
+
+	printk ("It is now safe to switch off your machine.\n");
+	while(1);
+}
+
+/*
+ * Handle miscellaneous OSS interrupts. Right now that's just sound
+ * and SCSI; everything else is routed to its own autovector IRQ.
+ */
+ 
+void oss_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int events;
+
+	events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
+	if (!events) return;
+
+#ifdef DEBUG_IRQS	
+	if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
+		printk("oss_irq: irq %d events = 0x%04X\n", irq,
+			(int) oss->irq_pending);
+	}
+#endif
+	/* FIXME: how do you clear a pending IRQ?    */
+
+	if (events & OSS_IP_SOUND) {
+		oss->irq_pending &= ~OSS_IP_SOUND;
+		/* FIXME: call sound handler */
+	} else if (events & OSS_IP_SCSI) {
+		oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
+		oss->irq_pending &= ~OSS_IP_SCSI;
+		mac_do_irq_list(IRQ_MAC_SCSI, regs);
+		oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
+	} else {
+		/* FIXME: error check here? */
+	}
+}
+
+/*
+ * Nubus IRQ handler, OSS style
+ *
+ * Unlike the VIA/RBV this is on its own autovector interupt level.
+ */
+
+void oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int events, irq_bit, i;
+
+	events = oss->irq_pending & OSS_IP_NUBUS;
+	if (!events) return;
+
+#ifdef DEBUG_NUBUS_INT
+	if (console_loglevel > 7) {
+		printk("oss_nubus_irq: events = 0x%04X\n", events);
+	}
+#endif
+	/* There are only six slots on the OSS, not seven */
+
+	for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) {
+		if (events & irq_bit) {
+			oss->irq_level[i] = OSS_IRQLEV_DISABLED;
+			oss->irq_pending &= ~irq_bit;
+			mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
+			oss->irq_level[i] = OSS_IRQLEV_NUBUS;
+		}
+	}
+}
+
+/*
+ * Enable an OSS interrupt
+ *
+ * It looks messy but it's rather straightforward. The switch() statement
+ * just maps the machspec interrupt numbers to the right OSS interrupt
+ * source (if the OSS handles that interrupt) and then sets the interrupt
+ * level for that source to nonzero, thus enabling the interrupt.
+ */
+
+void oss_irq_enable(int irq) {
+#ifdef DEBUG_IRQUSE
+	printk("oss_irq_enable(%d)\n", irq);
+#endif
+	switch(irq) {
+		case IRQ_SCC:
+		case IRQ_SCCA:
+		case IRQ_SCCB:
+			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
+			break;
+		case IRQ_MAC_ADB:
+			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
+			break;
+		case IRQ_MAC_SCSI:
+			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
+			break;
+		case IRQ_NUBUS_9:
+		case IRQ_NUBUS_A:
+		case IRQ_NUBUS_B:
+		case IRQ_NUBUS_C:
+		case IRQ_NUBUS_D:
+		case IRQ_NUBUS_E:
+			irq -= NUBUS_SOURCE_BASE;
+			oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
+			break;
+#ifdef DEBUG_IRQUSE
+		default:
+			printk("%s unknown irq %d\n",__FUNCTION__, irq);
+			break;
+#endif
+	}
+}
+
+/*
+ * Disable an OSS interrupt
+ *
+ * Same as above except we set the source's interrupt level to zero,
+ * to disable the interrupt.
+ */
+
+void oss_irq_disable(int irq) {
+#ifdef DEBUG_IRQUSE
+	printk("oss_irq_disable(%d)\n", irq);
+#endif
+	switch(irq) {
+		case IRQ_SCC:
+		case IRQ_SCCA:
+		case IRQ_SCCB:
+			oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED;
+			break;
+		case IRQ_MAC_ADB:
+			oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED;
+			break;
+		case IRQ_MAC_SCSI:
+			oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
+			break;
+		case IRQ_NUBUS_9:
+		case IRQ_NUBUS_A:
+		case IRQ_NUBUS_B:
+		case IRQ_NUBUS_C:
+		case IRQ_NUBUS_D:
+		case IRQ_NUBUS_E:
+			irq -= NUBUS_SOURCE_BASE;
+			oss->irq_level[irq] = OSS_IRQLEV_DISABLED;
+			break;
+#ifdef DEBUG_IRQUSE
+		default:
+			printk("%s unknown irq %d\n", __FUNCTION__, irq);
+			break;
+#endif
+	}
+}
+
+/*
+ * Clear an OSS interrupt
+ *
+ * Not sure if this works or not but it's the only method I could
+ * think of based on the contents of the mac_oss structure.
+ */
+
+void oss_irq_clear(int irq) {
+	/* FIXME: how to do this on OSS? */
+	switch(irq) {
+		case IRQ_SCC:
+		case IRQ_SCCA:
+		case IRQ_SCCB:
+			oss->irq_pending &= ~OSS_IP_IOPSCC;
+			break;
+		case IRQ_MAC_ADB:
+			oss->irq_pending &= ~OSS_IP_IOPISM;
+			break;
+		case IRQ_MAC_SCSI:
+			oss->irq_pending &= ~OSS_IP_SCSI;
+			break;
+		case IRQ_NUBUS_9:
+		case IRQ_NUBUS_A:
+		case IRQ_NUBUS_B:
+		case IRQ_NUBUS_C:
+		case IRQ_NUBUS_D:
+		case IRQ_NUBUS_E:
+			irq -= NUBUS_SOURCE_BASE;
+			oss->irq_pending &= ~(1 << irq);
+			break;
+	}
+}
+
+/*
+ * Check to see if a specific OSS interrupt is pending
+ */
+
+int oss_irq_pending(int irq)
+{
+	switch(irq) {
+		case IRQ_SCC:
+		case IRQ_SCCA:
+		case IRQ_SCCB:
+			return oss->irq_pending & OSS_IP_IOPSCC;
+			break;
+		case IRQ_MAC_ADB:
+			return oss->irq_pending & OSS_IP_IOPISM;
+			break;
+		case IRQ_MAC_SCSI:
+			return oss->irq_pending & OSS_IP_SCSI;
+			break;
+		case IRQ_NUBUS_9:
+		case IRQ_NUBUS_A:
+		case IRQ_NUBUS_B:
+		case IRQ_NUBUS_C:
+		case IRQ_NUBUS_D:
+		case IRQ_NUBUS_E:
+			irq -= NUBUS_SOURCE_BASE;
+			return oss->irq_pending & (1 << irq);
+			break;
+	}
+	return 0;
+}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/psc.c linux/arch/m68k/mac/psc.c
--- linux-2.2.18-pre15/arch/m68k/mac/psc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/psc.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,200 @@
+/*
+ *	Apple Peripheral System Controller (PSC)
+ *
+ *	The PSC is used on the AV Macs to control IO functions not handled
+ *	by the VIAs (Ethernet, DSP, SCC).
+ *
+ * TO DO:
+ *
+ * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be
+ * persisant interrupt conditions in those registers and I have no idea what
+ * they are. Granted it doesn't affect since we're not enabling any interrupts
+ * on those levels at the moment, but it would be nice to know. I have a feeling
+ * they aren't actually interrupt lines but data lines (to the DSP?)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/adb.h> 
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h> 
+#include <asm/mac_psc.h>
+
+#define DEBUG_PSC
+
+int psc_present;
+volatile __u8 *psc;
+
+void psc_irq(int, void *, struct pt_regs *);
+
+extern int console_loglevel;
+
+/*
+ * Debugging dump, used in various places to see what's going on.
+ */
+
+void psc_debug_dump(void)
+{
+	int	i;
+
+	if (!psc_present) return;
+	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
+		printk("PSC #%d:  IFR = 0x%02X IER = 0x%02X\n",
+			i >> 4,
+			(int) psc_read_byte(pIFRbase + i),
+			(int) psc_read_byte(pIERbase + i));
+	}
+}
+
+/*
+ * Try to kill all DMA channels on the PSC. Not sure how this his
+ * supposed to work; this is code lifted from macmace.c and then
+ * expanded to cover what I think are the other 7 channels.
+ */
+
+void psc_dma_die_die_die(void)
+{
+	int i;
+
+	printk("Killing all PSC DMA channels...");
+	for (i = 0 ; i < 9 ; i++) {
+		psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
+		psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
+		psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
+		psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
+	}
+	printk("done!\n");
+}
+
+/*
+ * Initialize the PSC. For now this just involves shutting down all
+ * interrupt sources using the IERs.
+ */
+
+__initfunc(void psc_init(void))
+{
+	int i;
+
+	if (macintosh_config->ident != MAC_MODEL_C660
+	 && macintosh_config->ident != MAC_MODEL_Q840)
+	{
+		psc = NULL;
+		psc_present = 0;
+		return;
+	}
+
+	/*
+	 * The PSC is always at the same spot, but using psc
+	 * keeps things consisant with the psc_xxxx functions.
+	 */
+
+	psc = (void *) PSC_BASE;
+	psc_present = 1;
+
+	printk("PSC detected at %p\n", psc);
+
+	psc_dma_die_die_die();
+
+#ifdef DEBUG_PSC
+	psc_debug_dump();
+#endif
+	/*
+	 * Mask and clear all possible interrupts
+	 */
+
+	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
+		psc_write_byte(pIERbase + i, 0x0F);
+		psc_write_byte(pIFRbase + i, 0x0F);
+	}
+}
+
+/*
+ * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
+ */
+
+__initfunc(void psc_register_interrupts(void))
+{
+	sys_request_irq(3, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
+			(void *) 0x30);
+	sys_request_irq(4, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
+			(void *) 0x40);
+	sys_request_irq(5, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
+			(void *) 0x50);
+	sys_request_irq(6, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
+			(void *) 0x60);
+}
+
+/*
+ * PSC interrupt handler. It's a lot like the VIA interrupt handler.
+ */
+
+void psc_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int pIFR	= pIFRbase + ((int) dev_id);
+	int pIER	= pIERbase + ((int) dev_id);
+	int base_irq	= irq << 3;
+	int irq_bit,i;
+	unsigned char events;
+
+#ifdef DEBUG_IRQS
+	printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n",
+		irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER));
+#endif
+
+	events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
+	if (!events) return;
+
+	for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) {
+	        if (events & irq_bit) {
+			psc_write_byte(pIER, irq_bit);
+			psc_write_byte(pIFR, irq_bit);
+			mac_do_irq_list(base_irq + i, regs);
+			psc_write_byte(pIER, irq_bit | 0x80);
+		}
+	}
+}
+
+void psc_irq_enable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIER	= pIERbase + (irq_src << 4);
+
+#ifdef DEBUG_IRQUSE
+	printk("psc_irq_enable(%d)\n", irq);
+#endif
+	psc_write_byte(pIER, (1 << irq_idx) | 0x80);
+}
+
+void psc_irq_disable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIER	= pIERbase + (irq_src << 4);
+
+#ifdef DEBUG_IRQUSE
+	printk("psc_irq_disable(%d)\n", irq);
+#endif
+	psc_write_byte(pIER, 1 << irq_idx);
+}
+
+void psc_irq_clear(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIFR	= pIERbase + (irq_src << 4);
+
+	psc_write_byte(pIFR, 1 << irq_idx);
+}
+
+int psc_irq_pending(int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIFR	= pIERbase + (irq_src << 4);
+
+	return psc_read_byte(pIFR) & (1 << irq_idx);
+}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/via.c linux/arch/m68k/mac/via.c
--- linux-2.2.18-pre15/arch/m68k/mac/via.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mac/via.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,788 @@
+/*
+ *	6522 Versatile Interface Adapter (VIA)
+ *
+ *	There are two of these on the Mac II. Some IRQ's are vectored
+ *	via them as are assorted bits and bobs - eg RTC, ADB.
+ *
+ * CSA: Motorola seems to have removed documentation on the 6522 from
+ * their web site; try
+ *     http://nerini.drf.com/vectrex/other/text/chips/6522/
+ *     http://www.zymurgy.net/classic/vic20/vicdet1.htm
+ * and
+ *     http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
+ * for info.  A full-text web search on 6522 AND VIA will probably also
+ * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
+ *
+ * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
+ * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h>
+#include <asm/machw.h> 
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
+
+volatile __u8 *via1, *via2;
+#if 0
+/* See note in mac_via.h about how this is possibly not useful */
+volatile long *via_memory_bogon=(long *)&via_memory_bogon;
+#endif
+int  rbv_present,via_alt_mapping;
+__u8 rbv_clear;
+
+/*
+ * Globals for accessing the VIA chip registers without having to
+ * check if we're hitting a real VIA or an RBV. Normally you could
+ * just hit the combined register (ie, vIER|rIER) but that seems to
+ * break on AV Macs...probably because they actually decode more than
+ * eight address bits. Why can't Apple engineers at least be
+ * _consistantly_ lazy?                          - 1999-05-21 (jmt)
+ */
+
+static int gIER,gIFR,gBufA,gBufB;
+
+/*
+ * Timer defs.
+ */
+
+#define TICK_SIZE		10000
+#define MAC_CLOCK_TICK		(783300/HZ)		/* ticks per HZ */
+#define MAC_CLOCK_LOW		(MAC_CLOCK_TICK&0xFF)
+#define MAC_CLOCK_HIGH		(MAC_CLOCK_TICK>>8)
+
+static int  nubus_active;
+
+void via_debug_dump(void);
+void via1_irq(int, void *, struct pt_regs *);
+void via2_irq(int, void *, struct pt_regs *);
+void via_nubus_irq(int, void *, struct pt_regs *);
+void via_irq_enable(int irq);
+void via_irq_disable(int irq);
+void via_irq_clear(int irq);
+
+extern void mac_bang(int, void *, struct pt_regs *);
+extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern int console_loglevel;
+extern int oss_present;
+
+/*
+ * Initialize the VIAs
+ *
+ * First we figure out where they actually _are_ as well as what type of
+ * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
+ * Then we pretty much clear them out and disable all IRQ sources.
+ *
+ * Note: the OSS is actually "detected" here and not in oss_init(). It just
+ *	 seems more logical to do it here since via_init() needs to know
+ *	 these things anyways.
+ */
+
+__initfunc(void via_init(void))
+{
+	switch(macintosh_config->via_type) {
+
+		/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
+
+		case MAC_VIA_IIci:	
+			via1 = (void *) VIA1_BASE;
+			if (macintosh_config->ident == MAC_MODEL_IIFX) {
+				via2 = NULL;
+				rbv_present = 0;
+				oss_present = 1;
+			} else {
+				via2 = (void *) RBV_BASE;
+				rbv_present = 1;
+				oss_present = 0;
+			}
+			if (macintosh_config->ident == MAC_MODEL_LCIII) {
+				rbv_clear = 0x00;
+			} else {
+				/* on most RBVs (& unlike the VIAs), you   */
+				/* need to set bit 7 when you write to IFR */
+				/* in order for your clear to occur.       */
+				rbv_clear = 0x80;
+			}
+			gIER = rIER;
+			gIFR = rIFR;
+			gBufA = rSIFR;
+			gBufB = rBufB;
+			break;
+
+		/* Quadra and early MacIIs agree on the VIA locations */
+
+		case MAC_VIA_QUADRA:
+		case MAC_VIA_II:
+			via1 = (void *) VIA1_BASE;
+			via2 = (void *) VIA2_BASE;
+			rbv_present = 0;
+			oss_present = 0;
+			rbv_clear = 0x00;
+			gIER = vIER;
+			gIFR = vIFR;
+			gBufA = vBufA;
+			gBufB = vBufB;
+			break;
+		default:
+			panic("UNKNOWN VIA TYPE");
+	}
+
+	printk("VIA1 at %p is a 6522 or clone\n", via1);
+
+	printk("VIA2 at %p is ", via2);
+	if (rbv_present) {
+		printk("an RBV\n");
+	} else if (oss_present) {
+		printk("an OSS\n");
+	} else {
+		printk("a 6522 or clone\n");
+	}
+
+#ifdef DEBUG_VIA
+	via_debug_dump();
+#endif
+
+	/*
+	 * Shut down all IRQ sources, reset the timers, and
+	 * kill the timer latch on VIA1.
+	 */
+
+	via_write(via1, vIER, 0x7F);	 
+	via_write(via1, vIFR, 0x7F);	 
+	via_write(via1, vT1LL, 0);
+	via_write(via1, vT1LH, 0);
+	via_write(via1, vT1CL, 0);
+	via_write(via1, vT1CH, 0);
+	via_write(via1, vT2CL, 0);
+	via_write(via1, vT2CH, 0);
+	via_write(via1, vACR, via_read(via1, vACR) & 0x3F);
+
+	/* 
+	 * SE/30: disable video IRQ
+	 * XXX: testing for SE/30 VBL
+	 */
+
+	if (macintosh_config->ident == MAC_MODEL_SE30) {
+		via_write(via1, vBufB, via_read(via1, vBufB) | 0x40);
+		via_write(via1, vDirB, via_read(via1, vDirB) | 0x40);
+	} 
+	
+	/*
+	 * Set the RTC bits to a known state: all lines to outputs and
+	 * RTC disabled (yes that's 0 to enable and 1 to disable).
+	 */
+
+	via_write(via1, vDirB, via_read(via1, vDirB) | (VIA1B_vRTCEnb |
+						        VIA1B_vRTCClk |
+						        VIA1B_vRTCData));
+	via_write(via1, vBufB, via_read(via1, vBufB) | (VIA1B_vRTCEnb |
+						        VIA1B_vRTCClk));
+
+	/* Everything below this point is VIA2/RBV only... */
+
+	if (oss_present) return;
+
+#if 1
+	/* Some machines support an alternate IRQ mapping that spreads  */
+	/* Ethernet and Sound out to their own autolevel IRQs and moves */
+	/* VIA1 to level 6. A/UX uses this mapping and we do too.  Note */
+	/* that the IIfx emulates this alternate mapping using the OSS. */
+
+	switch(macintosh_config->ident) {
+		case MAC_MODEL_C610:
+		case MAC_MODEL_Q610:
+		case MAC_MODEL_C650:
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			via_alt_mapping = 1;
+			via_write(via1, vDirB, via_read(via1, vDirB) | 0x40);
+			via_write(via1, vBufB, via_read(via1, vBufB) & ~0x40);
+			break;
+		default:
+			via_alt_mapping = 0;
+			break;
+	}
+#else
+	via_alt_mapping = 0;
+#endif
+
+	/*
+	 * Now initialize VIA2. For RBV we just kill all interrupts;
+	 * for a regular VIA we also reset the timers and stuff.
+	 */
+
+	via_write(via2, gIER, 0x7F);	 
+	via_write(via2, gIFR, 0x7F | rbv_clear);	 
+	if (!rbv_present) {
+		via_write(via2, vT1LL, 0);
+		via_write(via2, vT1LH, 0);
+		via_write(via2, vT1CL, 0);
+		via_write(via2, vT1CH, 0);
+		via_write(via2, vT2CL, 0);
+		via_write(via2, vT2CH, 0);
+		via_write(via2, vACR, via_read(via2, vACR) & 0x3F);
+	}
+}
+
+/*
+ * Start the 100 Hz clock
+ */
+
+__initfunc(void via_init_clock(void (*func)(int, void *, struct pt_regs *)))
+{	
+	via_write(via1, vACR, via_read(via1, vACR) | 0x40);
+	via_write(via1, vT1LL, MAC_CLOCK_LOW);
+	via_write(via1, vT1LH, MAC_CLOCK_HIGH);
+	via_write(via1, vT1CL, MAC_CLOCK_LOW);
+	via_write(via1, vT1CH, MAC_CLOCK_HIGH);
+	
+	request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func);
+}
+
+/*
+ * Register the interrupt dispatchers for VIA or RBV machines only.
+ */
+
+__initfunc(void via_register_interrupts(void))
+{
+	if (via_alt_mapping) {
+		sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+				"Software IRQ", (void *) via1);
+		sys_request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+				"VIA1 Dispatch", (void *) via1);
+	} else {
+		sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+				"VIA1 Dispatch", (void *) via1);
+#if 0 /* interferes with serial on some machines */
+		if (!psc_present) {
+			sys_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
+					"Off Switch", mac_bang);
+		}
+#endif
+	}
+	sys_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+			"VIA2 Dispatch", (void *) via2);
+	if (!psc_present) {
+		sys_request_irq(IRQ_AUTO_4, mac_SCC_handler, IRQ_FLG_LOCK,
+				"SCC Dispatch", mac_SCC_handler);
+	}
+	request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+			"Nubus Dispatch", (void *) via2);
+}
+
+/*
+ * Debugging dump, used in various places to see what's going on.
+ */
+
+void via_debug_dump(void)
+{
+	printk("VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+		via_read(via1, vDirA),
+		via_read(via1, vDirB),
+		via_read(via1, vACR));
+	printk("         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+		via_read(via1, vPCR),
+		via_read(via1, vIFR),
+		via_read(via1, vIER));
+	if (oss_present) {
+		printk("VIA2: <OSS>\n");
+	} else if (rbv_present) {
+		printk("VIA2:  IFR = 0x%02X  IER = 0x%02X\n",
+			via_read(via2, rIFR),
+			via_read(via2, rIER));
+		printk("      SIFR = 0x%02X SIER = 0x%02X\n",
+			via_read(via2, rSIFR),
+			via_read(via2, rSIER));
+	} else {
+		printk("VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+			via_read(via2, vDirA),
+			via_read(via2, vDirB),
+			via_read(via2, vACR));
+		printk("         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+			via_read(via2, vPCR),
+			via_read(via2, vIFR),
+			via_read(via2, vIER));
+	}
+}
+
+/*
+ * This is always executed with interrupts disabled.
+ *
+ * TBI: get time offset between scheduling timer ticks
+ */
+
+unsigned long mac_gettimeoffset (void)
+{
+	unsigned long ticks, offset = 0;
+
+	/* read VIA1 timer 2 current value */
+	ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8);
+	/* The probability of underflow is less than 2% */
+	if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50)
+		/* Check for pending timer interrupt in VIA1 IFR */
+		if (via_read(via1, vIFR) & 0x40)
+			offset = TICK_SIZE;
+
+	ticks = MAC_CLOCK_TICK - ticks;
+	ticks = ticks * 10000L / MAC_CLOCK_TICK;
+
+	return ticks + offset;
+}
+
+/*
+ * Flush the L2 cache on Macs that have it by flipping
+ * the system into 24-bit mode for an instant.
+ */
+
+void via_flush_cache(void)
+{
+	via_write(via2, gBufB, via_read(via2, vBufB) & ~VIA2B_vMode32);
+	via_write(via2, gBufB, via_read(via2, vBufB) | VIA2B_vMode32);
+}
+
+/*
+ * Return the status of the L2 cache on a IIci
+ */
+
+int via_get_cache_disable(void)
+{
+	/* Safeguard against being called accidentally */
+	if (!via2) {
+		printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n");
+		return 1;
+	}
+
+	return via_read(via2, gBufB) & VIA2B_vCDis;
+}
+
+/*
+ * VIA-based power switch, for machines that support it.
+ */
+
+void via_poweroff(void)
+{
+	if (rbv_present) {
+		via_write(via2, rBufB, via_read(via2, rBufB) & ~0x04);
+	} else {
+		/* Direction of vDirB is output */
+		via_write(via2, vDirB, via_read(via2, vDirB) | 0x04);
+		/* Send a value of 0 on that line */
+		via_write(via2, vBufB, via_read(via2, vBufB) & ~0x04);
+		/* Otherwise it prints "It is now.." then shuts off */
+		mdelay(1000);
+	}
+
+	/* We should never make it this far... */
+	printk ("It is now safe to switch off your machine.\n");
+	while(1);
+}
+
+/*
+ * Initialize VIA2 for Nubus access
+ */
+
+__initfunc(void via_nubus_init(void))
+{
+	nubus_active = 0;
+
+	/* unlock nubus transactions */
+
+	if (!rbv_present) {
+		/* set the line to be an output on non-RBV machines */
+		via_write(via2, vDirB, via_read(via2, vDirB) | 0x02);
+	}
+	via_write(via2, gBufB, via_read(via2, gBufB) | 0x02);
+
+	/* disable nubus slot interrupts. */
+	if (rbv_present) {
+		via_write(via2, rSIER, 0x7F); /* like VIA; bit 7=clr,set */
+		if ((via_read(via2, rSIER) & 0x7F) != 0) {
+			printk("SIER not behaving properly: "
+				"email <cananian@alumni.princeton.edu>\n");
+		}
+	} else {
+		via_write(via2, vBufA, 0xFF); /* active low irqs, force high */
+		via_write(via2, vDirA, 0xFF); /* ddr to output. */
+	}
+}
+
+/*
+ * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
+ * via6522.c :-), disable/pending masks added.
+ *
+ * The new interrupt architecture in macints.c takes care of a lot of the
+ * gruntwork for us, including tallying the interrupts and calling the
+ * handlers on the linked list. All we need to do here is basically generate
+ * the machspec interrupt number after clearing the interrupt.
+ */
+
+void via1_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events, mask;
+
+	mask = via_read(via1, vIER) & 0x7f;
+	events = via_read(via1, vIFR) & mask;
+	if (events == 0) return;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1)
+		if (events & irq_bit) {
+			via_write(via1, vIER, irq_bit);
+			via_write(via1, vIFR, irq_bit);
+			mac_do_irq_list(VIA1_SOURCE_BASE + i, regs);
+			via_write(via1, vIER, irq_bit | 0x80);
+		}
+
+	if (!oss_present) {
+		/* This (still) seems to be necessary to get IDE
+		   working.  However, if you enable VBL interrupts,
+		   you're screwed... */
+		/* FIXME: should we check the SLOTIRQ bit before
+                   pulling this stunt? */
+		via_irq_disable(IRQ_MAC_NUBUS);
+		via_irq_clear(IRQ_MAC_NUBUS);
+		mac_do_irq_list(IRQ_MAC_NUBUS, regs);
+		via_irq_enable(IRQ_MAC_NUBUS);
+	}
+}
+
+void via2_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events, mask;
+
+	mask = via_read(via2, gIER) & 0x7f;
+	events = via_read(via2, gIFR) & mask;
+	if (events == 0) return;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1)
+		if (events & irq_bit) {
+			via_write(via2, gIER, irq_bit);
+			via_write(via2, gIFR, irq_bit | rbv_clear);
+			mac_do_irq_list(VIA2_SOURCE_BASE + i, regs);
+			via_write(via2, gIER, irq_bit | 0x80);
+		}
+}
+
+/*
+ * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
+ * VIA2 dispatcher as a fast interrupt handler.
+ */
+
+void via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events;
+
+	events = ~via_read(via2, gBufA) & nubus_active;
+	if (events == 0) return;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) {
+		if (events & irq_bit) {
+			via_irq_disable(NUBUS_SOURCE_BASE + i);
+			/* FIXME: this does nothing.  Should we clear
+                           the SLOTIRQ bit here? */
+			via_irq_clear(NUBUS_SOURCE_BASE + i);
+			mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
+			via_irq_enable(NUBUS_SOURCE_BASE + i);
+		}
+	}
+}
+
+void via_irq_enable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+#ifdef DEBUG_IRQUSE
+	printk("via_irq_enable(%d)\n", irq);
+#endif
+
+	if (irq_src == 1) {
+		via_write(via1, vIER, irq_bit | 0x80);
+	} else if (irq_src == 2) {
+		/*
+		 * Set vPCR for SCSI interrupts (but not on RBV)
+		 */
+		if ((irq_idx == 0) && !rbv_present) {
+			if (macintosh_config->scsi_type == MAC_SCSI_OLD) {
+				/* CB2 (IRQ) indep. input, positive edge */
+				/* CA2 (DRQ) indep. input, positive edge */
+				via_write(via2, vPCR, 0x66);
+			} else {
+				/* CB2 (IRQ) indep. input, negative edge */
+				/* CA2 (DRQ) indep. input, negative edge */
+				via_write(via2, vPCR, 0x22);
+			}
+		}
+		via_write(via2, gIER, irq_bit | 0x80);
+	} else if (irq_src == 7) {
+		if (rbv_present) {
+			/* enable the slot interrupt. SIER works like IER. */
+			via_write(via2, rSIER, IER_SET_BIT(irq_idx));
+		} else {
+			/* Make sure the bit is an input, to enable the irq */
+			via_write(via2, vDirA,
+					via_read(via2, vDirA) & ~irq_bit);
+		}
+		nubus_active |= irq_bit;
+	}
+}
+
+void via_irq_disable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+#ifdef DEBUG_IRQUSE
+	printk("via_irq_disable(%d)\n", irq);
+#endif
+
+	if (irq_src == 1) {
+		via_write(via1, vIER, irq_bit);
+	} else if (irq_src == 2) {
+		via_write(via2, gIER, irq_bit);
+	} else if (irq_src == 7) {
+		if (rbv_present) {
+			/* disable the slot interrupt.  SIER works like IER. */
+			via_write(via2, rSIER, IER_CLR_BIT(irq_idx));
+		} else {
+			/* disable the nubus irq by changing dir to output */
+			via_write(via2, vBufA, ~nubus_active);
+			via_write(via2, vDirA, via_read(via2, vDirA) | irq_bit);
+		}
+		nubus_active &= ~irq_bit;
+	}
+}
+
+void via_irq_clear(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+	if (irq_src == 1) {
+		via_write(via1, vIFR, irq_bit);
+	} else if (irq_src == 2) {
+		via_write(via2, gIFR, irq_bit | rbv_clear);
+	} else if (irq_src == 7) {
+		/* FIXME: hmm.. */
+	}
+}
+
+/*
+ * Returns nonzero if an interrupt is pending on the given
+ * VIA/IRQ combination.
+ */
+
+int via_irq_pending(int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+	if (irq_src == 1) {
+		return via_read(via1, vIFR) & irq_bit;
+	} else if (irq_src == 2) {
+		return via_read(via2, gIFR) & irq_bit;
+	} else if (irq_src == 7) {
+		return (~via_read(via2, gBufA)) & irq_bit;
+	}
+	return 0;
+}
+
+void via_scsi_clear(void)
+{
+	volatile unsigned char deep_magic;
+
+#ifdef DEBUG_IRQUSE
+	printk("via_scsi_clear()\n");
+#endif
+
+	/* We handle this in oss.c , but this gets called in mac_scsinew.c */
+	if(oss_present) return;
+
+	if (rbv_present) {
+		via_write(via2, rIFR, (1<<3) | (1<<0) | rbv_clear);
+		deep_magic = via_read(via2, rBufB);
+	} else {
+		deep_magic = via_read(via2, vBufB);
+	}
+	mac_enable_irq((IRQ_IDX(IRQ_MAC_SCSI)));
+}
+
+/*
+ * PRAM/RTC access routines
+ *
+ * Must be called with interrupts disabled and
+ * the RTC should be enabled.
+ */
+
+static __u8 via_pram_readbyte(void)
+{
+	int	i,reg;
+	__u8	data;
+
+	reg = via_read(via1, vBufB) & ~VIA1B_vRTCClk;
+
+	/* Set the RTC data line to be an input. */
+
+	via_write(via1, vDirB, via_read(via1, vDirB) & ~VIA1B_vRTCData);
+
+	/* The bits of the byte come out in MSB order */
+
+	data = 0;
+	for (i = 0 ; i < 8 ; i++) {
+		via_write(via1, vBufB, reg);
+		via_write(via1, vBufB, reg | VIA1B_vRTCClk);
+		data = (data << 1) | (via_read(via1, vBufB) & VIA1B_vRTCData);
+	}
+
+	/* Return RTC data line to output state */
+
+	via_write(via1, vDirB, via_read(via1, vDirB) | VIA1B_vRTCData);
+
+	return data;
+}
+
+static void via_pram_writebyte(__u8 data)
+{
+	int	i,reg,bit;
+
+	reg = via_read(via1, vBufB) & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
+
+	/* The bits of the byte go in in MSB order */
+
+	for (i = 0 ; i < 8 ; i++) {
+		bit = data & 0x80? 1 : 0;
+		data <<= 1;
+		via_write(via1, vBufB, reg | bit);
+		via_write(via1, vBufB, reg | bit | VIA1B_vRTCClk);
+	}
+}
+
+/*
+ * Execute a PRAM/RTC command. For read commands
+ * data should point to a one-byte buffer for the
+ * resulting data. For write commands it should point
+ * to the data byte to for the command.
+ *
+ * This function disables all interrupts while running.
+ */
+
+void via_pram_command(int command, __u8 *data)
+{
+	unsigned long cpu_flags;
+	int	is_read,tmp;
+
+	save_flags(cpu_flags);
+	cli();
+
+	/* Enable the RTC and make sure the strobe line is high */
+
+	tmp = (via_read(via1, vBufB) | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
+	via_write(via1, vBufB, tmp);
+
+	if (command & 0xFF00) {		/* extended (two-byte) command */
+		via_pram_writebyte((command & 0xFF00) >> 8);
+		via_pram_writebyte(command & 0xFF);
+		is_read = command & 0x8000;
+	} else {			/* one-byte command */
+		via_pram_writebyte(command);
+		is_read = command & 0x80;
+	}
+	if (is_read) {
+		*data = via_pram_readbyte();
+	} else {
+		via_pram_writebyte(*data);
+	}
+
+	/* All done, disable the RTC */
+
+	via_write(via1, vBufB, via_read(via1, vBufB) | VIA1B_vRTCEnb);
+
+	restore_flags(cpu_flags);
+}
+
+/*
+ * Return the current time in seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+__u32 via_read_time(void)
+{
+	union {
+		__u8  cdata[4];
+		__u32 idata;
+	} result, last_result;
+	int	ct;
+
+	/*
+	 * The NetBSD guys say to loop until you get the same reading
+	 * twice in a row.
+	 */
+
+	ct = 0;
+	do {
+		if (++ct > 10) {
+			printk("via_read_time: couldn't get valid time, "
+			       "last read = 0x%08X and 0x%08X\n", last_result.idata,
+			       result.idata);
+			break;
+		}
+
+		last_result.idata = result.idata;
+		result.idata = 0;
+
+		via_pram_command(0x81, &result.cdata[3]);
+		via_pram_command(0x85, &result.cdata[2]);
+		via_pram_command(0x89, &result.cdata[1]);
+		via_pram_command(0x8D, &result.cdata[0]);
+	} while (result.idata != last_result.idata);
+
+	return result.idata;
+}
+
+/*
+ * Set the current time to a number of seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+void via_write_time(__u32 time)
+{
+	union {
+		__u8  cdata[4];
+		__u32 idata;
+	} data;
+	__u8	temp;
+
+	/* Clear the write protect bit */
+
+	temp = 0x55;
+	via_pram_command(0x35, &temp);
+
+	data.idata = time;
+	via_pram_command(0x01, &data.cdata[3]);
+	via_pram_command(0x05, &data.cdata[2]);
+	via_pram_command(0x09, &data.cdata[1]);
+	via_pram_command(0x0D, &data.cdata[0]);
+
+	/* Set the write protect bit */
+
+	temp = 0xD5;
+	via_pram_command(0x35, &temp);
+}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/via6522.c linux/arch/m68k/mac/via6522.c
--- linux-2.2.18-pre15/arch/m68k/mac/via6522.c	Thu Dec 17 09:06:29 1998
+++ linux/arch/m68k/mac/via6522.c	Wed Dec 31 16:00:00 1969
@@ -1,419 +0,0 @@
-/*
- *	6522 Versatile Interface Adapter (VIA)
- *
- *	There are two of these on the Mac II. Some IRQ's are vectored
- *	via them as are assorted bits and bobs - eg rtc, adb.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-
-#include <asm/adb.h> 
-#include <asm/bootinfo.h> 
-#include <asm/macintosh.h> 
-#include <asm/macints.h> 
-#include "via6522.h"
-#include <asm/mac_psc.h>
-
-volatile unsigned char *via1=(unsigned char *)VIABASE;
-volatile unsigned char *via2=(unsigned char *)VIABASE2;
-volatile unsigned char *psc=(unsigned char *)PSCBASE;
-
-volatile long *via_memory_bogon=(long *)&via_memory_bogon;
-
-unsigned char via1_clock, via1_datab;
-
-static int rbv=0;
-static int oss=0;
-
-extern void adb_interrupt(int slot, void *via, struct pt_regs *regs);
-
-/*
- * hardware reset vector
- */
-static void (*rom_reset)(void);
-
-/*
- * Timer defs.
- */
-#define MAC_CLOCK_TICK		(783300/HZ)	/* ticks per HZ */
-#define MAC_CLOCK_LOW		(MAC_CLOCK_TICK&0xFF)
-#define MAC_CLOCK_HIGH		(MAC_CLOCK_TICK>>8)
-
-
-void via_configure_base(void)
-{
-
-	switch(macintosh_config->via_type)
-	{
-		/*
-		 *      CI, SI, VX, LC 
-		 */
-		case MAC_VIA_IIci:
-			via1=(void *)0x50F00000;
-			via2=(void *)0x50F26000;
-			rbv=1;
-			if (macintosh_config->ident == MAC_MODEL_IIFX) {
-				via2=(void *)0x50F1A000;
-				oss=1;
-			}
-			break;
-		/*
-		 *      Quadra and early MacIIs agree on the VIA locations
-		 */
-		case MAC_VIA_QUADRA:
-		case MAC_VIA_II:
-			via1=(void *)0x50F00000;
-			via2=(void *)0x50F02000;
-			break;
-		default:
-	}
-}
-
-
-void via_init_clock(void (*func)(int, void *, struct pt_regs *))
-{	
-	unsigned char c;
-	
-	via1_clock=via_read(via1, vACR);
-	via1_datab=via_read(via1, vBufB);
-
-	/*
-	 * Tell what MacOS left us with
-	 */
-
-	printk("via_init: boot via1 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n",
-		(int)via1_clock, (int)via_read(via1, vPCR), 
-		(int)via_read(via1, vBufA), (int)via_read(via1, vDirA),
-		(int)via_read(via1, vBufB), (int)via_read(via1, vDirB));
-
-	if (rbv == 0)
-		printk("via_init: boot via2 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n",
-			(int)via_read(via2, vACR), (int)via_read(via2, vPCR), 
-			(int)via_read(via2, vBufA), (int)via_read(via2, vDirA),
-			(int)via_read(via2, vBufB), (int)via_read(via2, vDirB));
-
-	/*
-	 *	Shut it down
-	 */
-
-	via_write(via1,vIER, 0x7F);	 
-	
-	/*
-	 *	Kill the timers
-	 */
-	 
-	via_write(via1,vT1LL,0);
-	via_write(via1,vT1LH,0);
-	via_write(via1,vT1CL,0);
-	via_write(via1,vT1CH,0);
-	via_write(via1,vT2CL,0);
-	via_write(via1,vT2CH,0);
-	
-	/*
-	 *	Now do via2
-	 */
-
-	if(rbv==0) 
-	{
-		via_write(via2,vT1LL,0);
-		via_write(via2,vT1LH,0);
-		via_write(via2,vT1CL,0);
-		via_write(via2,vT1CH,0);
-		via_write(via2,vT2CL,0);
-		via_write(via2,vT2CH,0);
-		via_write(via2,vIER, 0x7F);	 
-	}
-	else if (oss==0)
-	{
-		/*
-		 *	Init the RBV chip a bit
-		 */
-		
-		via_write(via2, rIER,0x7F);
-	}
-
-	/*
-	 *	Disable the timer latches
-	 */
-	
-	c=via_read(via1,vACR);
-	via_write(via1,vACR,c&0x3F);
-
-	if(rbv==0) 
-	{
-		c=via_read(via2,vACR);
-		via_write(via2,vACR,c&0x3F);
-	}
-	  
-	/*
-	 *	Now start the clock - we want 100Hz
-	 */
-	 
-	via_write(via1,vACR,via_read(via1,vACR)|0x40);
-	
-	via_write(via1,vT1LL, MAC_CLOCK_LOW);
-	via_write(via1,vT1LH, MAC_CLOCK_HIGH);
-	via_write(via1,vT1CL, MAC_CLOCK_LOW);
-	via_write(via1,vT1CH, MAC_CLOCK_HIGH);
-	
-	/*
-	 *	And enable its interrupt
-	 */
-	
-	request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func);
-
-	/* 
-	 * SE/30: disable video int. 
-	 * XXX: testing for SE/30 VBL
-	 */
-
-	if (macintosh_config->ident == MAC_MODEL_SE30) {
-		c = via_read(via1, vBufB);
-		via_write(via1, vBufB, c|(0x40));
-		c = via_read(via1, vDirB);
-		via_write(via1, vDirB, c|(0x40));
-	} 
-
-#if 0 /* gone to mac_init_IRQ */
-	/*
-	 * Set vPCR for SCSI interrupts.
-	 *
-	 * That is: CA1 negative edge int., CA2 indep., positive edge int.;
-	 *	    CB1 negative edge int., CB2 indep., positive edge int..
-	 */
-        via_write(via2,vPCR, 0x66);
-#endif                                                                  
-
-}
-
-/*
- * TBI: get time offset between scheduling timer ticks
- */
-#define TICK_SIZE 10000
-  
-/* This is always executed with interrupts disabled.  */
-
-unsigned long mac_gettimeoffset (void)
-{
-	unsigned long ticks, offset = 0;
-
-	/* read VIA1 timer 2 current value */
-	ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8);
-	/* The probability of underflow is less than 2% */
-	if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50)
-		/* Check for pending timer interrupt in VIA1 IFR */
-		if (via_read(via1, vIFR) & 0x40)
-			offset = TICK_SIZE;
-
-	ticks = MAC_CLOCK_TICK - ticks;
-	ticks = ticks * 10000L / MAC_CLOCK_TICK;
-
-	return ticks + offset;
-}
-
-/*
- *	PSC (AV Macs; level 3-6): initialize interrupt enable registers
- */
-
-void psc_init(void)
-{
-	via_write(psc, pIER3, 0x01);
-	via_write(psc, pIER4, 0x09);
-	via_write(psc, pIER4, 0x86);
-	via_write(psc, pIER5, 0x03);
-	via_write(psc, pIER6, 0x07);
-}
-
-/*
- *	The power switch - yes it's software!
- */
-
-void mac_poweroff(void)
-{
-
-	/*
-	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
-	 * work using the ADB packet method.  --David Kilzer
-	 */
-
-	if (macintosh_config->adb_type == MAC_ADB_II)
-	{
-		if(rbv)	{
-			via_write(via2, rBufB, via_read(via2, rBufB)&~0x04);
-		} else {
-			/* Direction of vDirB is output */
-			via_write(via2,vDirB,via_read(via2,vDirB)|0x04);
-			/* Send a value of 0 on that line */
-			via_write(via2,vBufB,via_read(via2,vBufB)&~0x04);
-			/* Otherwise it prints "It is now.." then shuts off */
-			mdelay(1000);
-		}
-
-		/* We should never make it this far... */
-		printk ("It is now safe to switch off your machine.\n");
-
-		/* XXX - delay do we need to spin here ? */
-		while(1);	/* Just in case .. */
-	}
-
-	/*
-	 * Initially discovered this technique in the Mach kernel of MkLinux in
-	 * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c.  Found equivalent LinuxPPC
-	 * code in arch/ppc/kernel/setup.c, which also has a PMU technique for PowerBooks!
-	 * --David Kilzer
-	 */
-
-	else if (macintosh_config->adb_type == MAC_ADB_IISI
-	      || macintosh_config->adb_type == MAC_ADB_CUDA)
-	{
-		struct adb_request req;
-
-		/*
-		 * Print our "safe" message before we send the request
-		 * just in case the request never returns.
-		 */
-
-		printk ("It is now safe to switch off your machine.\n");
-
-		adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
-
-		printk ("ADB powerdown request sent.\n");
-		for (;;)
-		{
-			adb_poll();
-		}
-	}
-}
-
-/* 
- * Not all Macs support software power down; for the rest, just 
- * try the ROM reset vector ...
- */
-void mac_reset(void)
-{
-	/*
-	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
-	 * work using the ADB packet method.  --David Kilzer
-	 */
-
-	if (macintosh_config->adb_type == MAC_ADB_II)
-	{
-		unsigned long flags;
-		unsigned long *reset_hook;
-
-		/* need ROMBASE in booter */
-		/* indeed, plus need to MAP THE ROM !! */
-
-		if (mac_bi_data.rombase == 0)
-			mac_bi_data.rombase = 0x40800000;
-
-		/* works on some */
-		rom_reset = (void *) (mac_bi_data.rombase + 0xa);
-
-#if 0
-		/* testing, doesn't work on SE/30 either */
-		reset_hook = (unsigned long *) (mac_bi_data.rombase + 0x4);
-		printk("ROM reset hook: %p\n", *reset_hook);
-		rom_reset = *reset_hook;
-#endif
-		if (macintosh_config->ident == MAC_MODEL_SE30) {
-			/*
-			 * MSch: Machines known to crash on ROM reset ...
-			 */
-			printk("System halted.\n");
-			while(1);
-		} else {
-			save_flags(flags);
-			cli();
-
-			rom_reset();
-
-			restore_flags(flags);
-		}
-
-		/* We never make it this far... it usually panics above. */
-		printk ("Restart failed.  Please restart manually.\n");
-
-		/* XXX - delay do we need to spin here ? */
-		while(1);	/* Just in case .. */
-	}
-
-	/*
-	 * Initially discovered this technique in the Mach kernel of MkLinux in
-	 * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c.  Found equivalent LinuxPPC
-	 * code in arch/ppc/kernel/setup.c, which also has a PMU technique!
-	 * --David Kilzer
-	 * 
-	 * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines
-	 * but have no way to test this myself.  --DDK
-	 */
-
-	else if (macintosh_config->adb_type == MAC_ADB_IISI
-	      || macintosh_config->adb_type == MAC_ADB_CUDA)
-	{
-		struct adb_request req;
-
-		adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
-
-		printk ("Restart failed.  Please restart manually.\n");
-		for (;;)
-		{
-			adb_poll();
-		}
-	}
-}
-
-/*
- *	Set up the keyboard
- */
- 
-void via_setup_keyboard(void)
-{
-#if 0 /* moved to adb */
-	via1_func_tab.vector[2]=adb_interrupt;
-#else
-	request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, "adb interrupt",
-		    adb_interrupt);
-#endif
-}
-
-/*
- *	Floppy hook
- */
-
-void via1_set_head(int head)
-{
-	if(head==0)
-		via_write(via1, vBufA, via_read(via1, vBufA)&~0x20);
-	else
-		via_write(via1, vBufA, via_read(via1, vBufA)|0x20);
-}
-
-void nubus_init_via(void)
-{
-	if (rbv) {
-		if (oss==0) {
-			via_write(via2, rBufB, via_read(via2, rBufB)|0x02);
-			via_write(via2, rIER, 0x82);	/* Interrupts on */
-		}
-	} else {
-		/* Assert the nubus active */
-		via_write(via2, vDirB, via_read(via2, vDirB)|0x02);
-		via_write(via2, vBufB, via_read(via2, vBufB)|0x02);
-		/* Make the nubus interrupt source register all output (disable) */
-		/* via_write(via2, vDirA, 0xFF); */
-		via_write(via2, vIER, 0x82);	/* Interrupts on */
-	}
-
-	printk("nubus_init_via: via1 acr=%X datab=%X pcr=%X\n",
-		(int)via_read(via1, vACR), (int)via_read(via1, vBufB), 
-		(int)via_read(via1, vPCR));
-
-	if (rbv==0)
-		printk("nubus_init_via: via2 acr=%X datab=%X pcr=%X\n",
-			(int)via_read(via2, vACR), (int)via_read(via2, vBufB), 
-			(int)via_read(via2, vPCR));
-}
diff -urN linux-2.2.18-pre15/arch/m68k/mac/via6522.h linux/arch/m68k/mac/via6522.h
--- linux-2.2.18-pre15/arch/m68k/mac/via6522.h	Mon Oct  5 13:54:39 1998
+++ linux/arch/m68k/mac/via6522.h	Wed Dec 31 16:00:00 1969
@@ -1,131 +0,0 @@
-/*
- *	6522 Versatile Interface Adapter (VIA)
- *
- *	There are two of these on the Mac II. Some IRQ's are vectored
- *	via them as are assorted bits and bobs - eg rtc, adb. The picture
- *	is a bit incomplete as the Mac documentation doesnt cover this well
- */
- 
-#ifndef _ASM_VIA6522_H_
-#define _ASM_VIA6522_H_
-
-#define VIABASE		0x50F00000
-#define VIABASE2	0x50F02000
-
-/*
- *	Not all of these are true post MacII I think
- */
- 
-#define VIA1A_vSccWrReq	0x80	/* SCC write */
-#define VIA1A_vRev8	0x40	/* Revision 8 board ??? */
-#define VIA1A_vHeadSel	0x20	/* Head select for IWM */
-#define VIA1A_vOverlay	0x10
-#define VIA1A_vSync	0x08
-#define VIA1A_vVolume	0x07	/* Audio volume mask */
-	
-#define VIA1B_vSound	0x80	/* Audio on/off */
-#define VIA1B_vMystery	0x40
-#define VIA1B_vADBS2	0x20	/* ADB state 2 */
-#define VIA1B_vADBS1	0x10	/* ADB state 1 */
-#define VIA1B_vADBInt	0x08	/* ADB interrupt */
-#define VIA1B_vRTCEnb	0x04	/* Real time clock */
-#define VIA1B_vRTCClk	0x02
-#define VIA1B_vRTCData	0x01
-
-/*
- *	VIA2 A register is the interrupt lines raised off the nubus 
- *	slots.
- */
- 
-#define VIA2A_vIRQE	0x20
-#define VIA2A_vIRQD	0x10
-#define VIA2A_vIRQC	0x08
-#define VIA2A_vIRQB	0x04
-#define VIA2A_vIRQA	0x02
-#define VIA2A_vIRQ9	0x01
-
-/*
- *	Register B has the fun stuff in it
- */
-
-#define VIA2B_vMode32	0x08	/* 24/32bit switch - doubles as cache flush */
-#define VIA2B_vPower	0x04	/* Off switch */
-#define VIA2B_vBusLk	0x02	/* Nubus in use ?? */
-#define VIA2B_vCDis	0x01	/* Cache disable */
-
-/*
- *	The 6522 via is a 2MHz part, and needs a delay. MacOS seems to
- *	execute MOV (Ax),(Ax) for this... Oh and we can't use udelay
- *	here... see we need the via to calibrate the udelay loop ...
- */
-
-extern volatile long *via_memory_bogon;
- 
-extern __inline__ void via_write(volatile unsigned char *via,int reg, int v)
-{
-	*via_memory_bogon;
-	*via_memory_bogon;
-	*via_memory_bogon;
-	via[reg]=v;
-}
-
-extern __inline__ int via_read(volatile unsigned char *via,int reg)
-{
-	*via_memory_bogon;
-	*via_memory_bogon;
-	*via_memory_bogon;
-	return (int)via[reg];
-}
-
-extern volatile unsigned char *via1,*via2;
-
-/*
- *	6522 registers - see databook
- */
- 
-#define vBufB	0x0000
-#define vBufA	0x0200
-#define vDirB	0x0400
-#define vDirA	0x0600
-#define vT1CL	0x0800
-#define vT1CH	0x0a00
-#define vT1LL	0x0c00
-#define vT1LH	0x0e00
-#define vT2CL	0x1000
-#define vT2CH	0x1200
-#define vSR	0x1400
-#define vACR	0x1600
-#define vPCR	0x1800
-#define vIFR	0x1a00
-#define vIER	0x1c00 
-#define vANH	0x1e00  /* register A (no shake) */
-
-#define rBufB	0x00
-#define rBufA	0x02
-/*#define rIFR	0x03*/
-#define rIFR	0x1A03
-#define rVideo	0x10
-#define rSlot	0x12
-/*#define rIER	0x13*/
-#define rIER	0x1C13
-/*
-#define R_rIFR	0x03
-#define R_rIER	0x13
-#define W_rIFR	0x1A03
-#define W_rIER	0x1C13
-*/
-/*
- *	VIA interrupt
- */
- 
-struct via_irq_tab
-{
-	void (*vector[8])(int, void *, struct pt_regs *);
-};
-
-extern void via1_irq(int, void *, struct pt_regs *);
-extern void via2_irq(int, void *, struct pt_regs *);
-
-extern void via_setup_keyboard(void);
-
-#endif /* _ASM_VIA6522_H_ */
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/Makefile linux/arch/m68k/math-emu/Makefile
--- linux-2.2.18-pre15/arch/m68k/math-emu/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/Makefile	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.o:
+	$(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+
+#EXTRA_CFLAGS=-DFPU_EMU_DEBUG
+
+O_TARGET := mathemu.o
+O_OBJS := fp_entry.o fp_scan.o fp_util.o fp_move.o fp_movem.o \
+	  fp_cond.o fp_arith.o fp_log.o fp_trig.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_arith.c linux/arch/m68k/math-emu/fp_arith.c
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_arith.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_arith.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,700 @@
+/*
+
+   fp_arith.c: floating-point math routines for the Linux-m68k
+   floating point emulator.
+
+   Copyright (c) 1998-1999 David Huggins-Daines.
+
+   Somewhat based on the AlphaLinux floating point emulator, by David
+   Mosberger-Tang.
+
+   You may copy, modify, and redistribute this file under the terms of
+   the GNU General Public License, version 2, or any later version, at
+   your convenience.
+ */
+
+#include "fp_emu.h"
+#include "multi_arith.h"
+#include "fp_arith.h"
+
+const struct fp_ext fp_QNaN =
+{
+	0, 0, 0x7fff, { ~0 }
+};
+
+const struct fp_ext fp_Inf =
+{
+	0, 0, 0x7fff, { 0 }
+};
+
+/* let's start with the easy ones */
+
+struct fp_ext *
+fp_fabs(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fabs\n");
+
+	fp_monadic_check(dest, src);
+
+	dest->sign = 0;
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fneg(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fneg\n");
+
+	fp_monadic_check(dest, src);
+
+	dest->sign = !dest->sign;
+
+	return dest;
+}
+
+/* Now, the slightly harder ones */
+
+/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB,
+   FDSUB, and FCMP instructions. */
+
+struct fp_ext *
+fp_fadd(struct fp_ext *dest, struct fp_ext *src)
+{
+	int diff;
+
+	dprint(PINSTR, "fadd\n");
+
+	fp_dyadic_check(dest, src);
+
+	if (IS_INF(dest)) {
+		/* infinity - infinity == NaN */
+		if (IS_INF(src) && (src->sign != dest->sign))
+			fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_INF(src)) {
+		fp_copy_ext(dest, src);
+		return dest;
+	}
+
+	if (IS_ZERO(dest)) {
+		if (IS_ZERO(src)) {
+			if (src->sign != dest->sign) {
+				if (FPDATA->rnd == FPCR_ROUND_RM)
+					dest->sign = 1;
+				else
+					dest->sign = 0;
+			}
+		} else
+			fp_copy_ext(dest, src);
+		return dest;
+	}
+
+	dest->lowmant = src->lowmant = 0;
+
+	if ((diff = dest->exp - src->exp) > 0)
+		fp_denormalize(src, diff);
+	else if ((diff = -diff) > 0)
+		fp_denormalize(dest, diff);
+
+	if (dest->sign == src->sign) {
+		if (fp_addmant(dest, src))
+			if (!fp_addcarry(dest))
+				return dest;
+	} else {
+		if (dest->mant.m64 < src->mant.m64) {
+			fp_submant(dest, src, dest);
+			dest->sign = !dest->sign;
+		} else
+			fp_submant(dest, dest, src);
+	}
+
+	return dest;
+}
+
+/* fp_fsub: Implementes the kernel of the FSUB, FSSUB, and FDSUB
+   instructions.
+
+   Remember that the arguments are in assembler-syntax order! */
+
+struct fp_ext *
+fp_fsub(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fsub ");
+
+	src->sign = !src->sign;
+	return fp_fadd(dest, src);
+}
+
+
+struct fp_ext *
+fp_fcmp(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fcmp ");
+
+	FPDATA->temp[1] = *dest;
+	src->sign = !src->sign;
+	return fp_fadd(&FPDATA->temp[1], src);
+}
+
+struct fp_ext *
+fp_ftst(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "ftst\n");
+
+	(void)dest;
+
+	return src;
+}
+
+struct fp_ext *
+fp_fmul(struct fp_ext *dest, struct fp_ext *src)
+{
+	union fp_mant128 temp;
+	int exp;
+
+	dprint(PINSTR, "fmul\n");
+
+	fp_dyadic_check(dest, src);
+
+	/* calculate the correct sign now, as it's necessary for infinities */
+	dest->sign = src->sign ^ dest->sign;
+
+	/* Handle infinities */
+	if (IS_INF(dest)) {
+		if (IS_ZERO(src))
+			fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_INF(src)) {
+		if (IS_ZERO(dest))
+			fp_set_nan(dest);
+		else
+			fp_copy_ext(dest, src);
+		return dest;
+	}
+
+	/* Of course, as we all know, zero * anything = zero.  You may
+	   not have known that it might be a positive or negative
+	   zero... */
+	if (IS_ZERO(dest) || IS_ZERO(src)) {
+		dest->exp = 0;
+		dest->mant.m64 = 0;
+		dest->lowmant = 0;
+
+		return dest;
+	}
+
+	exp = dest->exp + src->exp - 0x3ffe;
+
+	/* shift up the mantissa for denormalized numbers,
+	   so that the highest bit is set, this makes the
+	   shift of the result below easier */
+	if ((long)dest->mant.m32[0] >= 0)
+		exp -= fp_overnormalize(dest);
+	if ((long)src->mant.m32[0] >= 0)
+		exp -= fp_overnormalize(src);
+
+	/* now, do a 64-bit multiply with expansion */
+	fp_multiplymant(&temp, dest, src);
+
+	/* normalize it back to 64 bits and stuff it back into the
+	   destination struct */
+	if ((long)temp.m32[0] > 0) {
+		exp--;
+		fp_putmant128(dest, &temp, 1);
+	} else
+		fp_putmant128(dest, &temp, 0);
+
+	if (exp >= 0x7fff) {
+		fp_set_ovrflw(dest);
+		return dest;
+	}
+	dest->exp = exp;
+	if (exp < 0) {
+		fp_set_sr(FPSR_EXC_UNFL);
+		fp_denormalize(dest, -exp);
+	}
+
+	return dest;
+}
+
+/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and
+   FSGLDIV instructions.
+
+   Note that the order of the operands is counter-intuitive: instead
+   of src / dest, the result is actually dest / src. */
+
+struct fp_ext *
+fp_fdiv(struct fp_ext *dest, struct fp_ext *src)
+{
+	union fp_mant128 temp;
+	int exp;
+
+	dprint(PINSTR, "fdiv\n");
+
+	fp_dyadic_check(dest, src);
+
+	/* calculate the correct sign now, as it's necessary for infinities */
+	dest->sign = src->sign ^ dest->sign;
+
+	/* Handle infinities */
+	if (IS_INF(dest)) {
+		/* infinity / infinity = NaN (quiet, as always) */
+		if (IS_INF(src))
+			fp_set_nan(dest);
+		/* infinity / anything else = infinity (with approprate sign) */
+		return dest;
+	}
+	if (IS_INF(src)) {
+		/* anything / infinity = zero (with appropriate sign) */
+		dest->exp = 0;
+		dest->mant.m64 = 0;
+		dest->lowmant = 0;
+
+		return dest;
+	}
+
+	/* zeroes */
+	if (IS_ZERO(dest)) {
+		/* zero / zero = NaN */
+		if (IS_ZERO(src))
+			fp_set_nan(dest);
+		/* zero / anything else = zero */
+		return dest;
+	}
+	if (IS_ZERO(src)) {
+		/* anything / zero = infinity (with appropriate sign) */
+		fp_set_sr(FPSR_EXC_DZ);
+		dest->exp = 0x7fff;
+		dest->mant.m64 = 0;
+
+		return dest;
+	}
+
+	exp = dest->exp - src->exp + 0x3fff;
+
+	/* shift up the mantissa for denormalized numbers,
+	   so that the highest bit is set, this makes lots
+	   of things below easier */
+	if ((long)dest->mant.m32[0] >= 0)
+		exp -= fp_overnormalize(dest);
+	if ((long)src->mant.m32[0] >= 0)
+		exp -= fp_overnormalize(src);
+
+	/* now, do the 64-bit divide */
+	fp_dividemant(&temp, dest, src);
+
+	/* normalize it back to 64 bits and stuff it back into the
+	   destination struct */
+	if (!temp.m32[0]) {
+		exp--;
+		fp_putmant128(dest, &temp, 32);
+	} else
+		fp_putmant128(dest, &temp, 31);
+
+	if (exp >= 0x7fff) {
+		fp_set_ovrflw(dest);
+		return dest;
+	}
+	dest->exp = exp;
+	if (exp < 0) {
+		fp_set_sr(FPSR_EXC_UNFL);
+		fp_denormalize(dest, -exp);
+	}
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsglmul(struct fp_ext *dest, struct fp_ext *src)
+{
+	int exp;
+
+	dprint(PINSTR, "fsglmul\n");
+
+	fp_dyadic_check(dest, src);
+
+	/* calculate the correct sign now, as it's necessary for infinities */
+	dest->sign = src->sign ^ dest->sign;
+
+	/* Handle infinities */
+	if (IS_INF(dest)) {
+		if (IS_ZERO(src))
+			fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_INF(src)) {
+		if (IS_ZERO(dest))
+			fp_set_nan(dest);
+		else
+			fp_copy_ext(dest, src);
+		return dest;
+	}
+
+	/* Of course, as we all know, zero * anything = zero.  You may
+	   not have known that it might be a positive or negative
+	   zero... */
+	if (IS_ZERO(dest) || IS_ZERO(src)) {
+		dest->exp = 0;
+		dest->mant.m64 = 0;
+		dest->lowmant = 0;
+
+		return dest;
+	}
+
+	exp = dest->exp + src->exp - 0x3ffe;
+
+	/* do a 32-bit multiply */
+	fp_mul64(dest->mant.m32[0], dest->mant.m32[1],
+		 dest->mant.m32[0] & 0xffffff00,
+		 src->mant.m32[0] & 0xffffff00);
+
+	if (exp >= 0x7fff) {
+		fp_set_ovrflw(dest);
+		return dest;
+	}
+	dest->exp = exp;
+	if (exp < 0) {
+		fp_set_sr(FPSR_EXC_UNFL);
+		fp_denormalize(dest, -exp);
+	}
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src)
+{
+	int exp;
+	unsigned long quot, rem;
+
+	dprint(PINSTR, "fsgldiv\n");
+
+	fp_dyadic_check(dest, src);
+
+	/* calculate the correct sign now, as it's necessary for infinities */
+	dest->sign = src->sign ^ dest->sign;
+
+	/* Handle infinities */
+	if (IS_INF(dest)) {
+		/* infinity / infinity = NaN (quiet, as always) */
+		if (IS_INF(src))
+			fp_set_nan(dest);
+		/* infinity / anything else = infinity (with approprate sign) */
+		return dest;
+	}
+	if (IS_INF(src)) {
+		/* anything / infinity = zero (with appropriate sign) */
+		dest->exp = 0;
+		dest->mant.m64 = 0;
+		dest->lowmant = 0;
+
+		return dest;
+	}
+
+	/* zeroes */
+	if (IS_ZERO(dest)) {
+		/* zero / zero = NaN */
+		if (IS_ZERO(src))
+			fp_set_nan(dest);
+		/* zero / anything else = zero */
+		return dest;
+	}
+	if (IS_ZERO(src)) {
+		/* anything / zero = infinity (with appropriate sign) */
+		fp_set_sr(FPSR_EXC_DZ);
+		dest->exp = 0x7fff;
+		dest->mant.m64 = 0;
+
+		return dest;
+	}
+
+	exp = dest->exp - src->exp + 0x3fff;
+
+	dest->mant.m32[0] &= 0xffffff00;
+	src->mant.m32[0] &= 0xffffff00;
+
+	/* do the 32-bit divide */
+	if (dest->mant.m32[0] >= src->mant.m32[0]) {
+		fp_sub64(dest->mant, src->mant);
+		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
+		dest->mant.m32[0] = 0x80000000 | (quot >> 1);
+		dest->mant.m32[1] = (quot & 1) | rem;	/* only for rounding */
+	} else {
+		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
+		dest->mant.m32[0] = quot;
+		dest->mant.m32[1] = rem;		/* only for rounding */
+		exp--;
+	}
+
+	if (exp >= 0x7fff) {
+		fp_set_ovrflw(dest);
+		return dest;
+	}
+	dest->exp = exp;
+	if (exp < 0) {
+		fp_set_sr(FPSR_EXC_UNFL);
+		fp_denormalize(dest, -exp);
+	}
+
+	return dest;
+}
+
+/* fp_roundint: Internal rounding function for use by several of these
+   emulated instructions.
+
+   This one rounds off the fractional part using the rounding mode
+   specified. */
+
+static void fp_roundint(struct fp_ext *dest, int mode)
+{
+	union fp_mant64 oldmant;
+	unsigned long mask;
+
+	if (!fp_normalize_ext(dest))
+		return;
+
+	/* infinities and zeroes */
+	if (IS_INF(dest) || IS_ZERO(dest)) 
+		return;
+
+	/* first truncate the lower bits */
+	oldmant = dest->mant;
+	switch (dest->exp) {
+	case 0 ... 0x3ffe:
+		dest->mant.m64 = 0;
+		break;
+	case 0x3fff ... 0x401e:
+		dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp);
+		dest->mant.m32[1] = 0;
+		if (oldmant.m64 == dest->mant.m64)
+			return;
+		break;
+	case 0x401f ... 0x403e:
+		dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp);
+		if (oldmant.m32[1] == dest->mant.m32[1])
+			return;
+		break;
+	default:
+		return;
+	}
+	fp_set_sr(FPSR_EXC_INEX2);
+
+	/* We might want to normalize upwards here... however, since
+	   we know that this is only called on the output of fp_fdiv,
+	   or with the input to fp_fint or fp_fintrz, and the inputs
+	   to all these functions are either normal or denormalized
+	   (no subnormals allowed!), there's really no need.
+
+	   In the case of fp_fdiv, observe that 0x80000000 / 0xffff =
+	   0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the
+	   smallest possible normal dividend and the largest possible normal
+	   divisor will still produce a normal quotient, therefore, (normal
+	   << 64) / normal is normal in all cases) */
+
+	switch (mode) {
+	case FPCR_ROUND_RN:
+		switch (dest->exp) {
+		case 0 ... 0x3ffd:
+			return;
+		case 0x3ffe:
+			/* As noted above, the input is always normal, so the
+			   guard bit (bit 63) is always set.  therefore, the
+			   only case in which we will NOT round to 1.0 is when
+			   the input is exactly 0.5. */
+			if (oldmant.m64 == (1ULL << 63))
+				return;
+			break;
+		case 0x3fff ... 0x401d:
+			mask = 1 << (0x401d - dest->exp);
+			if (!(oldmant.m32[0] & mask))
+				return;
+			if (oldmant.m32[0] & (mask << 1))
+				break;
+			if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) &&
+					!oldmant.m32[1])
+				return;
+			break;
+		case 0x401e:
+			if (!(oldmant.m32[1] >= 0))
+				return;
+			if (oldmant.m32[0] & 1)
+				break;
+			if (!(oldmant.m32[1] << 1))
+				return;
+			break;
+		case 0x401f ... 0x403d:
+			mask = 1 << (0x403d - dest->exp);
+			if (!(oldmant.m32[1] & mask))
+				return;
+			if (oldmant.m32[1] & (mask << 1))
+				break;
+			if (!(oldmant.m32[1] << (dest->exp - 0x401d)))
+				return;
+			break;
+		default:
+			return;
+		}
+		break;
+	case FPCR_ROUND_RZ:
+		return;
+	default:
+		if (dest->sign ^ (mode - FPCR_ROUND_RM))
+			break;
+		return;
+	}
+
+	switch (dest->exp) {
+	case 0 ... 0x3ffe:
+		dest->exp = 0x3fff;
+		dest->mant.m64 = 1ULL << 63;
+		break;
+	case 0x3fff ... 0x401e:
+		mask = 1 << (0x401e - dest->exp);
+		if (dest->mant.m32[0] += mask)
+			break;
+		dest->mant.m32[0] = 0x80000000;
+		dest->exp++;
+		break;
+	case 0x401f ... 0x403e:
+		mask = 1 << (0x403e - dest->exp);
+		if (dest->mant.m32[1] += mask)
+			break;
+		if (dest->mant.m32[0] += 1)
+                        break;
+		dest->mant.m32[0] = 0x80000000;
+                dest->exp++;
+		break;
+	}
+}
+
+/* modrem_kernel: Implementation of the FREM and FMOD instructions
+   (which are exactly the same, except for the rounding used on the
+   intermediate value) */
+
+static struct fp_ext *
+modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode)
+{
+	struct fp_ext tmp;
+
+	fp_dyadic_check(dest, src);
+
+	/* Infinities and zeros */
+	if (IS_INF(dest) || IS_ZERO(src)) {
+		fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_ZERO(dest) || IS_INF(src))
+		return dest;
+
+	/* FIXME: there is almost certainly a smarter way to do this */
+	fp_copy_ext(&tmp, dest);
+	fp_fdiv(&tmp, src);		/* NOTE: src might be modified */
+	fp_roundint(&tmp, mode);
+	fp_fmul(&tmp, src);
+	fp_fsub(dest, &tmp);
+
+	/* set the quotient byte */
+	fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7));
+	return dest;
+}
+
+/* fp_fmod: Implements the kernel of the FMOD instruction.
+
+   Again, the argument order is backwards.  The result, as defined in
+   the Motorola manuals, is:
+
+   fmod(src,dest) = (dest - (src * floor(dest / src))) */
+
+struct fp_ext *
+fp_fmod(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fmod\n");
+	return modrem_kernel(dest, src, FPCR_ROUND_RZ);
+}
+
+/* fp_frem: Implements the kernel of the FREM instruction.
+
+   frem(src,dest) = (dest - (src * round(dest / src)))
+ */
+
+struct fp_ext *
+fp_frem(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "frem\n");
+	return modrem_kernel(dest, src, FPCR_ROUND_RN);
+}
+
+struct fp_ext *
+fp_fint(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fint\n");
+
+	fp_copy_ext(dest, src);
+
+	fp_roundint(dest, FPDATA->rnd);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fintrz(struct fp_ext *dest, struct fp_ext *src)
+{
+	dprint(PINSTR, "fintrz\n");
+
+	fp_copy_ext(dest, src);
+
+	fp_roundint(dest, FPCR_ROUND_RZ);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fscale(struct fp_ext *dest, struct fp_ext *src)
+{
+	int scale, oldround;
+
+	dprint(PINSTR, "fscale\n");
+
+	fp_dyadic_check(dest, src);
+
+	/* Infinities */
+	if (IS_INF(src)) {
+		fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_INF(dest))
+		return dest;
+
+	/* zeroes */
+	if (IS_ZERO(src) || IS_ZERO(dest))
+		return dest;
+
+	/* Source exponent out of range */
+	if (src->exp >= 0x400c) {
+		fp_set_ovrflw(dest);
+		return dest;
+	}
+
+	/* src must be rounded with round to zero. */
+	oldround = FPDATA->rnd;
+	FPDATA->rnd = FPCR_ROUND_RZ;
+	scale = fp_conv_ext2long(src);
+	FPDATA->rnd = oldround;
+
+	/* new exponent */
+	scale += dest->exp;
+
+	if (scale >= 0x7fff) {
+		fp_set_ovrflw(dest);
+	} else if (scale <= 0) {
+		fp_set_sr(FPSR_EXC_UNFL);
+		fp_denormalize(dest, -scale);
+	} else
+		dest->exp = scale;
+
+	return dest;
+}
+
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_arith.h linux/arch/m68k/math-emu/fp_arith.h
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_arith.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_arith.h	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,52 @@
+/*
+
+   fp_arith.h: floating-point math routines for the Linux-m68k
+   floating point emulator.
+
+   Copyright (c) 1998 David Huggins-Daines.
+
+   Somewhat based on the AlphaLinux floating point emulator, by David
+   Mosberger-Tang.
+
+   You may copy, modify, and redistribute this file under the terms of
+   the GNU General Public License, version 2, or any later version, at
+   your convenience.
+
+ */
+
+#ifndef FP_ARITH_H
+#define FP_ARITH_H
+
+/* easy ones */
+struct fp_ext *
+fp_fabs(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fneg(struct fp_ext *dest, struct fp_ext *src);
+
+/* straightforward arithmetic */
+struct fp_ext *
+fp_fadd(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fsub(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fcmp(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_ftst(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fmul(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fdiv(struct fp_ext *dest, struct fp_ext *src);
+
+/* ones that do rounding and integer conversions */
+struct fp_ext *
+fp_fmod(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_frem(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fint(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fintrz(struct fp_ext *dest, struct fp_ext *src);
+struct fp_ext *
+fp_fscale(struct fp_ext *dest, struct fp_ext *src);
+
+#endif	/* FP_ARITH__H */
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_cond.S linux/arch/m68k/math-emu/fp_cond.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_cond.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_cond.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,334 @@
+/*
+ * fp_cond.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fp_emu.h"
+#include "fp_decode.h"
+
+	.globl	fp_fscc, fp_fbccw, fp_fbccl
+
+#ifdef FPU_EMU_DEBUG
+fp_fnop:
+	printf	PDECODE,"fnop\n"
+	jra	fp_end
+#else
+#define fp_fnop fp_end
+#endif
+
+fp_fbccw:
+	tst.w	%d2
+	jeq	fp_fnop
+	printf	PDECODE,"fbccw "
+	fp_get_pc %a0
+	lea	(-2,%a0,%d2.w),%a0
+	jra	1f
+
+fp_fbccl:
+	printf	PDECODE,"fbccl "
+	fp_get_pc %a0
+	move.l	%d2,%d0
+	swap	%d0
+	fp_get_instr_word %d0,fp_err_ua1
+	lea	(-2,%a0,%d0.l),%a0
+1:	printf	PDECODE,"%x",1,%a0
+	move.l	%d2,%d0
+	swap	%d0
+	jsr	fp_compute_cond
+	tst.l	%d0
+	jeq	1f
+	fp_put_pc %a0,1
+1:	printf	PDECODE,"\n"
+	jra	fp_end
+
+fp_fdbcc:
+	printf	PDECODE,"fdbcc "
+	fp_get_pc %a1				| calculate new pc
+	fp_get_instr_word %d0,fp_err_ua1
+	add.w	%d0,%a1
+	fp_decode_addr_reg
+	printf	PDECODE,"d%d,%x\n",2,%d0,%a1
+	swap	%d1				| test condition in %d1
+	tst.w	%d1
+	jne	2f
+	move.l	%d0,%d1
+	jsr	fp_get_data_reg
+	subq.w	#1,%d0
+	jcs	1f
+	fp_put_pc %a1,1
+1:	jsr	fp_put_data_reg
+2:	jra	fp_end
+
+| set flags for decode macros for fs<cc>
+do_fscc=1
+do_no_pc_mode=1
+
+fp_fscc:
+	printf	PDECODE,"fscc "
+	move.l	%d2,%d0
+	jsr	fp_compute_cond
+	move.w	%d0,%d1
+	swap	%d1
+
+	| decode addressing mode
+	fp_decode_addr_mode
+
+	.long 	fp_data, fp_fdbcc
+	.long 	fp_indirect, fp_postinc
+	.long 	fp_predecr, fp_disp16
+	.long 	fp_extmode0, fp_extmode1
+
+	| addressing mode: data register direct
+fp_data:
+	fp_mode_data_direct
+	move.w	%d0,%d1			| save register nr
+	jsr	fp_get_data_reg
+	swap	%d1
+	move.b	%d1,%d0
+	swap	%d1
+	jsr	fp_put_data_reg
+	printf	PDECODE,"\n"
+	jra	fp_end
+
+fp_indirect:
+	fp_mode_addr_indirect
+	jra	fp_do_scc
+
+fp_postinc:
+	fp_mode_addr_indirect_postinc
+	jra	fp_do_scc
+
+fp_predecr:
+	fp_mode_addr_indirect_predec
+	jra	fp_do_scc
+
+fp_disp16:
+	fp_mode_addr_indirect_disp16
+	jra	fp_do_scc
+
+fp_extmode0:
+	fp_mode_addr_indirect_extmode0
+	jra	fp_do_scc
+
+fp_extmode1:
+	bfextu	%d2{#13,#3},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fp_absolute_short, fp_absolute_long
+	.long	fp_ill, fp_ill		| NOTE: jump here to ftrap.x
+	.long	fp_ill, fp_ill
+	.long	fp_ill, fp_ill
+
+fp_absolute_short:
+	fp_mode_abs_short
+	jra	fp_do_scc
+
+fp_absolute_long:
+	fp_mode_abs_long
+|	jra	fp_do_scc
+
+fp_do_scc:
+	swap	%d1
+	putuser.b %d1,(%a0),fp_err_ua1,%a0
+	printf	PDECODE,"\n"
+	jra	fp_end
+
+
+#define tst_NAN	btst #24,%d1
+#define tst_Z	btst #26,%d1
+#define tst_N	btst #27,%d1
+
+fp_compute_cond:
+	move.l	(FPD_FPSR,FPDATA),%d1
+	btst	#4,%d0
+	jeq	1f
+	tst_NAN
+	jeq	1f
+	bset	#15,%d1
+	bset	#7,%d1
+	move.l	%d1,(FPD_FPSR,FPDATA)
+1:	and.w	#0xf,%d0
+	jmp	([0f:w,%pc,%d0.w*4])
+
+	.align	4
+0:
+	.long	fp_f  , fp_eq , fp_ogt, fp_oge
+	.long	fp_olt, fp_ole, fp_ogl, fp_or
+	.long	fp_un , fp_ueq, fp_ugt, fp_uge
+	.long	fp_ult, fp_ule, fp_ne , fp_t
+
+fp_f:
+	moveq	#0,%d0
+	rts
+
+fp_eq:
+	moveq	#0,%d0
+	tst_Z
+	jeq	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_ogt:
+	moveq	#0,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	tst_N
+	jne	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_oge:
+	moveq	#-1,%d0
+	tst_Z
+	jne	2f
+	tst_NAN
+	jne	1f
+	tst_N
+	jeq	2f
+1:	moveq	#0,%d0
+2:	rts
+
+fp_olt:
+	moveq	#0,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	tst_N
+	jeq	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_ole:
+	moveq	#-1,%d0
+	tst_Z
+	jne	2f
+	tst_NAN
+	jne	1f
+	tst_N
+	jne	2f
+1:	moveq	#0,%d0
+2:	rts
+
+fp_ogl:
+	moveq	#0,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_or:
+	moveq	#0,%d0
+	tst_NAN
+	jne	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_un:
+	moveq	#0,%d0
+	tst_NAN
+	jeq	1f
+	moveq	#-1,%d0
+	rts
+
+fp_ueq:
+	moveq	#-1,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	moveq	#0,%d0
+1:	rts
+
+fp_ugt:
+	moveq	#-1,%d0
+	tst_NAN
+	jne	2f
+	tst_N
+	jne	1f
+	tst_Z
+	jeq	2f
+1:	moveq	#0,%d0
+2:	rts
+
+fp_uge:
+	moveq	#-1,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	tst_N
+	jeq	1f
+	moveq	#0,%d0
+1:	rts
+
+fp_ult:
+	moveq	#-1,%d0
+	tst_NAN
+	jne	2f
+	tst_Z
+	jne	1f
+	tst_N
+	jne	2f
+1:	moveq	#0,%d0
+2:	rts
+
+fp_ule:
+	moveq	#-1,%d0
+	tst_NAN
+	jne	1f
+	tst_Z
+	jne	1f
+	tst_N
+	jne	1f
+	moveq	#0,%d0
+1:	rts
+
+fp_ne:
+	moveq	#0,%d0
+	tst_Z
+	jne	1f
+	moveq	#-1,%d0
+1:	rts
+
+fp_t:
+	moveq	#-1,%d0
+	rts
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_decode.h linux/arch/m68k/math-emu/fp_decode.h
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_decode.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_decode.h	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,417 @@
+/*
+ * fp_decode.h
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FP_DECODE_H
+#define _FP_DECODE_H
+
+/* These macros do the dirty work of the instr decoding, several variables
+ * can be defined in the source file to modify the work of these macros,
+ * currently the following variables are used:
+ * ...
+ * The register usage:
+ * d0 - will contain source operand for data direct mode,
+ *	otherwise scratch register
+ * d1 - upper 16bit are reserved for caller
+ *	lower 16bit may contain further arguments,
+ *	is destroyed during decoding
+ * d2 - contains first two instruction words,
+ *	first word will be used for extension word
+ * a0 - will point to source/dest operand for any indirect mode
+ *	otherwise scratch register
+ * a1 - scratch register
+ * a2 - base addr to the task structure
+ *
+ * the current implementation doesn't check for every disallowed
+ * addressing mode (e.g. pc relative modes as destination), as long
+ * as it only means a new addressing mode, which should not appear
+ * in a program and that doesn't crash the emulation, I think it's
+ * not a problem to allow these modes.
+ */
+
+do_fmovem=0
+do_fmovem_cr=0
+do_no_pc_mode=0
+do_fscc=0
+
+| first decoding of the instr type
+| this seperates the conditional instr
+.macro	fp_decode_cond_instr_type
+	bfextu	%d2{#8,#2},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+|	.long	"f<op>","fscc/fdbcc"
+|	.long	"fbccw","fbccl"
+.endm
+
+| second decoding of the instr type
+| this seperates most move instr
+.macro	fp_decode_move_instr_type
+	bfextu	%d2{#16,#3},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+|	.long	"f<op> fpx,fpx","invalid instr"
+|	.long	"f<op> <ea>,fpx","fmove fpx,<ea>"
+|	.long	"fmovem <ea>,fpcr","fmovem <ea>,fpx"
+|	.long	"fmovem fpcr,<ea>","fmovem fpx,<ea>"
+.endm
+
+| extract the source specifier, specifies
+| either source fp register or data format
+.macro	fp_decode_sourcespec
+	bfextu	%d2{#19,#3},%d0
+.endm
+
+| decode destination format for fmove reg,ea
+.macro	fp_decode_dest_format
+	bfextu	%d2{#19,#3},%d0
+.endm
+
+| decode source register for fmove reg,ea
+.macro	fp_decode_src_reg
+	bfextu	%d2{#22,#3},%d0
+.endm
+
+| extract the addressing mode
+| it depends on the instr which of the modes is valid
+.macro	fp_decode_addr_mode
+	bfextu	%d2{#10,#3},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+|	.long	"data register direct","addr register direct"
+|	.long	"addr register indirect"
+|	.long	"addr register indirect postincrement"
+|	.long	"addr register indirect predecrement"
+|	.long	"addr register + index16"
+|	.long	"extension mode1","extension mode2"
+.endm
+
+| extract the register for the addressing mode
+.macro	fp_decode_addr_reg
+	bfextu	%d2{#13,#3},%d0
+.endm
+
+| decode the 8bit diplacement from the brief extension word
+.macro	fp_decode_disp8
+	move.b	%d2,%d0
+	ext.w	%d0
+.endm
+
+| decode the index of the brief/full extension word
+.macro	fp_decode_index
+	bfextu	%d2{#17,#3},%d0		| get the register nr
+	btst	#15,%d2			| test for data/addr register
+	jne	1\@f
+	printf	PDECODE,"d%d",1,%d0
+	jsr	fp_get_data_reg
+	jra	2\@f
+1\@:	printf	PDECODE,"a%d",1,%d0
+	jsr	fp_get_addr_reg
+	move.l	%a0,%d0
+2\@:
+debug	lea	"'l'.w,%a0"
+	btst	#11,%d2			| 16/32 bit size?
+	jne	3\@f
+debug	lea	"'w'.w,%a0"
+	ext.l	%d0
+3\@:	printf	PDECODE,":%c",1,%a0
+	move.w	%d2,%d1			| scale factor
+	rol.w	#7,%d1
+	and.w	#3,%d1
+debug	move.l	"%d1,-(%sp)"
+debug	ext.l	"%d1"
+	printf	PDECODE,":%d",1,%d1
+debug	move.l	"(%sp)+,%d1"
+	lsl.l	%d1,%d0
+.endm
+
+| decode the base displacement size
+.macro	fp_decode_basedisp
+	bfextu	%d2{#26,#2},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+|	.long	"reserved","null displacement"
+|	.long	"word displacement","long displacement"
+.endm
+
+.macro	fp_decode_outerdisp
+	bfextu	%d2{#30,#2},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+|	.long	"no memory indirect action/reserved","null outer displacement"
+|	.long	"word outer displacement","long outer displacement"
+.endm
+
+| get the extension word and test for brief or full extension type
+.macro	fp_get_test_extword label
+	fp_get_instr_word %d2,fp_err_ua1
+	btst	#8,%d2
+	jne	\label
+.endm
+
+
+| test if %pc is the base register for the indirect addr mode
+.macro	fp_test_basereg_d16	label
+	btst	#20,%d2
+	jeq	\label
+.endm
+
+| test if %pc is the base register for one of the extended modes
+.macro	fp_test_basereg_ext	label
+	btst	#19,%d2
+	jeq	\label
+.endm
+
+.macro	fp_test_suppr_index label
+	btst	#6,%d2
+	jne	\label
+.endm
+
+
+| addressing mode: data register direct
+.macro	fp_mode_data_direct
+	fp_decode_addr_reg
+	printf	PDECODE,"d%d",1,%d0
+.endm
+
+| addressing mode: address register indirect
+.macro	fp_mode_addr_indirect
+	fp_decode_addr_reg
+	printf	PDECODE,"(a%d)",1,%d0
+	jsr	fp_get_addr_reg
+.endm
+
+| adjust stack for byte moves from/to stack
+.macro	fp_test_sp_byte_move
+	.if	!do_fmovem
+	.if	do_fscc
+	move.w	#6,%d1
+	.endif
+	cmp.w	#7,%d0
+	jne	1\@f
+	.if	!do_fscc
+	cmp.w	#6,%d1
+	jne	1\@f
+	.endif
+	move.w	#4,%d1
+1\@:
+	.endif
+.endm
+
+| addressing mode: address register indirect with postincrement
+.macro	fp_mode_addr_indirect_postinc
+	fp_decode_addr_reg
+	printf	PDECODE,"(a%d)+",1,%d0
+	fp_test_sp_byte_move
+	jsr	fp_get_addr_reg
+	move.l	%a0,%a1			| save addr
+	.if	do_fmovem
+	lea	(%a0,%d1.w*4),%a0
+	.if	!do_fmovem_cr
+	lea	(%a0,%d1.w*8),%a0
+	.endif
+	.else
+	add.w	(fp_datasize,%d1.w*2),%a0
+	.endif
+	jsr	fp_put_addr_reg
+	move.l	%a1,%a0
+.endm
+
+| addressing mode: address register indirect with predecrement
+.macro	fp_mode_addr_indirect_predec
+	fp_decode_addr_reg
+	printf	PDECODE,"-(a%d)",1,%d0
+	fp_test_sp_byte_move
+	jsr	fp_get_addr_reg
+	.if	do_fmovem
+	.if	!do_fmovem_cr
+	lea	(-12,%a0),%a1		| setup to addr of 1st reg to move
+	neg.w	%d1
+	lea	(%a0,%d1.w*4),%a0
+	add.w	%d1,%d1
+	lea	(%a0,%d1.w*4),%a0
+	jsr	fp_put_addr_reg
+	move.l	%a1,%a0
+	.else
+	neg.w	%d1
+	lea	(%a0,%d1.w*4),%a0
+	jsr	fp_put_addr_reg
+	.endif
+	.else
+	sub.w	(fp_datasize,%d1.w*2),%a0
+	jsr	fp_put_addr_reg
+	.endif
+.endm
+
+| addressing mode: address register/programm counter indirect
+|		   with 16bit displacement
+.macro	fp_mode_addr_indirect_disp16
+	.if	!do_no_pc_mode
+	fp_test_basereg_d16 1f
+	printf	PDECODE,"pc"
+	fp_get_pc %a0
+	jra	2f
+	.endif
+1:	fp_decode_addr_reg
+	printf	PDECODE,"a%d",1,%d0
+	jsr	fp_get_addr_reg
+2:	fp_get_instr_word %a1,fp_err_ua1
+	printf	PDECODE,"@(%x)",1,%a1
+	add.l	%a1,%a0
+.endm
+
+| perform preindex (if I/IS == 0xx and xx != 00)
+.macro	fp_do_preindex
+	moveq	#3,%d0
+	and.w	%d2,%d0
+	jeq	1f
+	btst	#2,%d2
+	jne	1f
+	printf	PDECODE,")@("
+	getuser.l (%a1),%a1,fp_err_ua1,%a1
+debug	jra	"2f"
+1:	printf	PDECODE,","
+2:
+.endm
+
+| perform postindex (if I/IS == 1xx)
+.macro	fp_do_postindex
+	btst	#2,%d2
+	jeq	1f
+	printf	PDECODE,")@("
+	getuser.l (%a1),%a1,fp_err_ua1,%a1
+debug	jra	"2f"
+1:	printf	PDECODE,","
+2:
+.endm
+
+| all other indirect addressing modes will finally end up here
+.macro	fp_mode_addr_indirect_extmode0
+	.if	!do_no_pc_mode
+	fp_test_basereg_ext 1f
+	printf	PDECODE,"pc"
+	fp_get_pc %a0
+	jra	2f
+	.endif
+1:	fp_decode_addr_reg
+	printf	PDECODE,"a%d",1,%d0
+	jsr	fp_get_addr_reg
+2:	move.l	%a0,%a1
+	swap	%d2
+	fp_get_test_extword 3f
+	| addressing mode: address register/programm counter indirect
+	|		   with index and 8bit displacement
+	fp_decode_disp8
+debug	ext.l	"%d0"
+	printf	PDECODE,"@(%x,",1,%d0
+	add.w	%d0,%a1
+	fp_decode_index
+	add.l	%d0,%a1
+	printf	PDECODE,")"
+	jra	9f
+3:	| addressing mode: address register/programm counter memory indirect
+	|		   with base and/or outer displacement
+	btst	#7,%d2			| base register suppressed?
+	jeq	1f
+	printf	PDECODE,"!"
+	sub.l	%a1,%a1
+1:	printf	PDECODE,"@("
+	fp_decode_basedisp
+
+	.long	fp_ill,1f
+	.long	2f,3f
+
+#ifdef FPU_EMU_DEBUG
+1:	printf	PDECODE,"0"		| null base displacement
+	jra	1f
+#endif
+2:	fp_get_instr_word %a0,fp_err_ua1 | 16bit base displacement
+	printf	PDECODE,"%x:w",1,%a0
+	jra	4f
+3:	fp_get_instr_long %a0,fp_err_ua1 | 32bit base displacement
+	printf	PDECODE,"%x:l",1,%a0
+4:	add.l	%a0,%a1
+1:
+	fp_do_postindex
+	fp_test_suppr_index 1f
+	fp_decode_index
+	add.l	%d0,%a1
+1:	fp_do_preindex
+
+	fp_decode_outerdisp
+
+	.long	5f,1f
+	.long	2f,3f
+
+#ifdef FPU_EMU_DEBUG
+1:	printf	PDECODE,"0"		| null outer displacement
+	jra	1f
+#endif
+2:	fp_get_instr_word %a0,fp_err_ua1 | 16bit outer displacement
+	printf	PDECODE,"%x:w",1,%a0
+	jra	4f
+3:	fp_get_instr_long %a0,fp_err_ua1 | 32bit outer displacement
+	printf	PDECODE,"%x:l",1,%a0
+4:	add.l	%a0,%a1
+1:
+5:	printf	PDECODE,")"
+9:	move.l	%a1,%a0
+	swap	%d2
+.endm
+
+| get the absolute short address from user space
+.macro	fp_mode_abs_short
+	fp_get_instr_word %a0,fp_err_ua1
+	printf	PDECODE,"%x.w",1,%a0
+.endm
+
+| get the absolute long address from user space
+.macro	fp_mode_abs_long
+	fp_get_instr_long %a0,fp_err_ua1
+	printf	PDECODE,"%x.l",1,%a0
+.endm
+
+#endif /* _FP_DECODE_H */
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_emu.h linux/arch/m68k/math-emu/fp_emu.h
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_emu.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_emu.h	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,137 @@
+/*
+ * fp_emu.h
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FP_EMU_H
+#define _FP_EMU_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/math-emu.h>
+
+#define IS_INF(a) ((a)->exp == 0x7fff)
+#define IS_ZERO(a) ((a)->mant.m64 == 0)
+
+
+#define fp_set_sr(bit) ({					\
+	FPDATA->fpsr |= 1 << (bit);				\
+})
+
+#define fp_set_quotient(quotient) ({				\
+	FPDATA->fpsr &= 0xff00ffff;				\
+	FPDATA->fpsr |= ((quotient) & 0xff) << 16;		\
+})
+
+/* linkage for several useful functions */
+
+/* Normalize the extended struct, return 0 for a NaN */
+#define fp_normalize_ext(fpreg) ({				\
+	register struct fp_ext *reg asm ("a0") = fpreg;		\
+	register int res asm ("d0");				\
+								\
+	asm volatile ("jsr fp_conv_ext2ext"			\
+			: "=d" (res) : "a" (reg)		\
+			: "a1", "d1", "d2", "memory");		\
+	res;							\
+})
+
+#define fp_copy_ext(dest, src) ({				\
+	*dest = *src;						\
+})
+
+#define fp_monadic_check(dest, src) ({				\
+	fp_copy_ext(dest, src);					\
+	if (!fp_normalize_ext(dest))				\
+		return dest;					\
+})
+
+#define fp_dyadic_check(dest, src) ({				\
+	if (!fp_normalize_ext(dest))				\
+		return dest;					\
+	if (!fp_normalize_ext(src)) {				\
+		fp_copy_ext(dest, src);				\
+		return dest;					\
+	}							\
+})
+
+extern const struct fp_ext fp_QNaN;
+extern const struct fp_ext fp_Inf;
+
+#define fp_set_nan(dest) ({					\
+	fp_set_sr(FPSR_EXC_OPERR);				\
+	*dest = fp_QNaN;					\
+})
+
+/* TODO check rounding mode? */
+#define fp_set_ovrflw(dest) ({					\
+	fp_set_sr(FPSR_EXC_OVFL);				\
+	dest->exp = 0x7fff;					\
+	dest->mant.m64 = 0;					\
+})
+
+#define fp_conv_ext2long(src) ({				\
+	register struct fp_ext *__src asm ("a0") = src;		\
+	register int __res asm ("d0");				\
+								\
+	asm volatile ("jsr fp_conv_ext2long"			\
+			: "=d" (__res) : "a" (__src)		\
+			: "a1", "d1", "d2", "memory");		\
+	__res;							\
+})
+
+#else /* __ASSEMBLY__ */
+
+#include "../kernel/m68k_defs.h"
+#include <asm/math-emu.h>
+
+/*
+ * set, reset or clear a bit in the fp status register
+ */
+.macro	fp_set_sr	bit
+	bset	#(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA)
+.endm
+
+.macro	fp_clr_sr	bit
+	bclr	#(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA)
+.endm
+
+.macro	fp_tst_sr	bit
+	btst	#(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA)
+.endm
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _FP_EMU_H */
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_entry.S linux/arch/m68k/math-emu/fp_entry.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_entry.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_entry.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,324 @@
+/*
+ * fp_emu.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/linkage.h>
+#include <asm/entry.h>
+
+#include "fp_emu.h"
+
+	.globl	SYMBOL_NAME(fpu_emu)
+	.globl	fp_debugprint
+	.globl	fp_err_ua1,fp_err_ua2
+
+	.text
+SYMBOL_NAME_LABEL(fpu_emu)
+	SAVE_ALL_INT
+	GET_CURRENT(%d0)
+
+#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060)
+        tst.l	SYMBOL_NAME(m68k_is040or060)
+        jeq	1f
+#endif
+#if defined(CPU_M68040_OR_M68060)
+	move.l	(FPS_PC2,%sp),(FPS_PC,%sp)
+#endif
+1:
+	| emulate the instruction
+	jsr	fp_scan
+
+#if defined(CONFIG_M68060)
+#if !defined(CPU_M68060_ONLY)
+	btst	#3,SYMBOL_NAME(m68k_cputype)+3
+	jeq	1f
+#endif
+	btst	#7,(FPS_SR,%sp)
+	jne	fp_sendtrace060
+#endif
+1:
+	| emulation successful?
+	tst.l	%d0
+	jeq	SYMBOL_NAME(ret_from_exception)
+
+	| send some signal to program here
+
+	jra	SYMBOL_NAME(ret_from_exception)
+
+	| we jump here after an access error while trying to access
+	| user space, we correct stackpointer and send a SIGSEGV to
+	| the user process
+fp_err_ua2:
+	addq.l	#4,%sp
+fp_err_ua1:
+	addq.l	#4,%sp
+	move.l	%a0,-(%sp)
+	pea	SEGV_MAPERR
+	pea	SIGSEGV
+	jsr	SYMBOL_NAME(fpemu_signal)
+	add.w	#12,%sp
+	jra	SYMBOL_NAME(ret_from_exception)
+
+#if defined(CONFIG_M68060)
+	| send a trace signal if we are debugged
+	| it does not really belong here, but...
+fp_sendtrace060:
+	move.l	(FPS_PC,%sp),-(%sp)
+	pea	TRAP_TRACE
+	pea	SIGTRAP
+	jsr	SYMBOL_NAME(fpemu_signal)
+	add.w	#12,%sp
+	jra	SYMBOL_NAME(ret_from_exception)
+#endif
+
+	.globl	fp_get_data_reg, fp_put_data_reg
+	.globl	fp_get_addr_reg, fp_put_addr_reg
+
+	| Entry points to get/put a register. Some of them can be get/put
+	| directly, others are on the stack, as we read/write the stack
+	| directly here, these function may only be called from within
+	| instruction decoding, otherwise the stack pointer is incorrect
+	| and the stack gets corrupted.
+fp_get_data_reg:
+	jmp	([0f:w,%pc,%d0.w*4])
+
+	.align	4
+0:
+	.long	fp_get_d0, fp_get_d1
+	.long	fp_get_d2, fp_get_d3
+	.long	fp_get_d4, fp_get_d5
+	.long	fp_get_d6, fp_get_d7
+
+fp_get_d0:
+	move.l	(PT_D0+8,%sp),%d0
+	printf	PREGISTER,"{d0->%08x}",1,%d0
+	rts
+
+fp_get_d1:
+	move.l	(PT_D1+8,%sp),%d0
+	printf	PREGISTER,"{d1->%08x}",1,%d0
+	rts
+
+fp_get_d2:
+	move.l	(PT_D2+8,%sp),%d0
+	printf	PREGISTER,"{d2->%08x}",1,%d0
+	rts
+
+fp_get_d3:
+	move.l	%d3,%d0
+	printf	PREGISTER,"{d3->%08x}",1,%d0
+	rts
+
+fp_get_d4:
+	move.l	%d4,%d0
+	printf	PREGISTER,"{d4->%08x}",1,%d0
+	rts
+
+fp_get_d5:
+	move.l	%d5,%d0
+	printf	PREGISTER,"{d5->%08x}",1,%d0
+	rts
+
+fp_get_d6:
+	move.l	%d6,%d0
+	printf	PREGISTER,"{d6->%08x}",1,%d0
+	rts
+
+fp_get_d7:
+	move.l	%d7,%d0
+	printf	PREGISTER,"{d7->%08x}",1,%d0
+	rts
+
+fp_put_data_reg:
+	jmp	([0f:w,%pc,%d1.w*4])
+
+	.align	4
+0:
+	.long	fp_put_d0, fp_put_d1
+	.long	fp_put_d2, fp_put_d3
+	.long	fp_put_d4, fp_put_d5
+	.long	fp_put_d6, fp_put_d7
+
+fp_put_d0:
+	printf	PREGISTER,"{d0<-%08x}",1,%d0
+	move.l	%d0,(PT_D0+8,%sp)
+	rts
+
+fp_put_d1:
+	printf	PREGISTER,"{d1<-%08x}",1,%d0
+	move.l	%d0,(PT_D1+8,%sp)
+	rts
+
+fp_put_d2:
+	printf	PREGISTER,"{d2<-%08x}",1,%d0
+	move.l	%d0,(PT_D2+8,%sp)
+	rts
+
+fp_put_d3:
+	printf	PREGISTER,"{d3<-%08x}",1,%d0
+|	move.l	%d0,%d3
+	move.l	%d0,(PT_D3+8,%sp)
+	rts
+
+fp_put_d4:
+	printf	PREGISTER,"{d4<-%08x}",1,%d0
+|	move.l	%d0,%d4
+	move.l	%d0,(PT_D4+8,%sp)
+	rts
+
+fp_put_d5:
+	printf	PREGISTER,"{d5<-%08x}",1,%d0
+|	move.l	%d0,%d5
+	move.l	%d0,(PT_D5+8,%sp)
+	rts
+
+fp_put_d6:
+	printf	PREGISTER,"{d6<-%08x}",1,%d0
+	move.l	%d0,%d6
+	rts
+
+fp_put_d7:
+	printf	PREGISTER,"{d7<-%08x}",1,%d0
+	move.l	%d0,%d7
+	rts
+
+fp_get_addr_reg:
+	jmp	([0f:w,%pc,%d0.w*4])
+
+	.align	4
+0:
+	.long	fp_get_a0, fp_get_a1
+	.long	fp_get_a2, fp_get_a3
+	.long	fp_get_a4, fp_get_a5
+	.long	fp_get_a6, fp_get_a7
+
+fp_get_a0:
+	move.l	(PT_A0+8,%sp),%a0
+	printf	PREGISTER,"{a0->%08x}",1,%a0
+	rts
+
+fp_get_a1:
+	move.l	(PT_A1+8,%sp),%a0
+	printf	PREGISTER,"{a1->%08x}",1,%a0
+	rts
+
+fp_get_a2:
+	move.l	(PT_A2+8,%sp),%a0
+	printf	PREGISTER,"{a2->%08x}",1,%a0
+	rts
+
+fp_get_a3:
+	move.l	%a3,%a0
+	printf	PREGISTER,"{a3->%08x}",1,%a0
+	rts
+
+fp_get_a4:
+	move.l	%a4,%a0
+	printf	PREGISTER,"{a4->%08x}",1,%a0
+	rts
+
+fp_get_a5:
+	move.l	%a5,%a0
+	printf	PREGISTER,"{a5->%08x}",1,%a0
+	rts
+
+fp_get_a6:
+	move.l	%a6,%a0
+	printf	PREGISTER,"{a6->%08x}",1,%a0
+	rts
+
+fp_get_a7:
+	move.l	%usp,%a0
+	printf	PREGISTER,"{a7->%08x}",1,%a0
+	rts
+
+fp_put_addr_reg:
+	jmp	([0f:w,%pc,%d0.w*4])
+
+	.align	4
+0:
+	.long	fp_put_a0, fp_put_a1
+	.long	fp_put_a2, fp_put_a3
+	.long	fp_put_a4, fp_put_a5
+	.long	fp_put_a6, fp_put_a7
+
+fp_put_a0:
+	printf	PREGISTER,"{a0<-%08x}",1,%a0
+	move.l	%a0,(PT_A0+8,%sp)
+	rts
+
+fp_put_a1:
+	printf	PREGISTER,"{a1<-%08x}",1,%a0
+	move.l	%a0,(PT_A1+8,%sp)
+	rts
+
+fp_put_a2:
+	printf	PREGISTER,"{a2<-%08x}",1,%a0
+	move.l	%a0,(PT_A2+8,%sp)
+	rts
+
+fp_put_a3:
+	printf	PREGISTER,"{a3<-%08x}",1,%a0
+	move.l	%a0,%a3
+	rts
+
+fp_put_a4:
+	printf	PREGISTER,"{a4<-%08x}",1,%a0
+	move.l	%a0,%a4
+	rts
+
+fp_put_a5:
+	printf	PREGISTER,"{a5<-%08x}",1,%a0
+	move.l	%a0,%a5
+	rts
+
+fp_put_a6:
+	printf	PREGISTER,"{a6<-%08x}",1,%a0
+	move.l	%a0,%a6
+	rts
+
+fp_put_a7:
+	printf	PREGISTER,"{a7<-%08x}",1,%a0
+	move.l	%a0,%usp
+	rts
+
+	.data
+	.align	4
+
+fp_debugprint:
+|	.long	PMDECODE
+	.long	PMINSTR+PMDECODE+PMCONV+PMNORM
+|	.long	PMCONV+PMNORM+PMINSTR
+|	.long	0
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_log.c linux/arch/m68k/math-emu/fp_log.c
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_log.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_log.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,142 @@
+/*
+
+  fp_trig.c: floating-point math routines for the Linux-m68k
+  floating point emulator.
+
+  Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel.
+
+  I hereby give permission, free of charge, to copy, modify, and
+  redistribute this software, in source or binary form, provided that
+  the above copyright notice and the following disclaimer are included
+  in all such copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL
+  OR IMPLIED.
+
+*/
+
+#include "fp_emu.h"
+
+struct fp_ext *
+fp_fsqrt(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsqrt\n");
+
+	fp_monadic_check(dest, src);
+
+	if (IS_ZERO(dest))
+		return dest;
+
+	if (dest->sign) {
+		fp_set_nan(dest);
+		return dest;
+	}
+	if (IS_INF(dest))
+		return dest;
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fetoxm1(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fetoxm1\n");
+
+	fp_monadic_check(dest, src);
+
+	if (IS_ZERO(dest))
+		return dest;
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fetox(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fetox\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_ftwotox(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("ftwotox\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_ftentox(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("ftentox\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_flogn(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("flogn\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_flognp1(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("flognp1\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_flog10(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("flog10\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_flog2(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("flog2\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fgetexp(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fgetexp\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fgetman(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fgetman\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_move.S linux/arch/m68k/math-emu/fp_move.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_move.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_move.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,244 @@
+/*
+ * fp_move.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fp_emu.h"
+#include "fp_decode.h"
+
+do_no_pc_mode=1
+
+	.globl	fp_fmove_fp2mem
+
+fp_fmove_fp2mem:
+	clr.b	(2+FPD_FPSR,FPDATA)
+	fp_decode_dest_format
+	move.w	%d0,%d1			| store data size twice in %d1
+	swap	%d1			| one can be trashed below
+	move.w	%d0,%d1
+#ifdef FPU_EMU_DEBUG
+	lea	0f,%a0
+	clr.l	%d0
+	move.b	(%a0,%d1.w),%d0
+	printf	PDECODE,"fmove.%c ",1,%d0
+	fp_decode_src_reg
+	printf	PDECODE,"fp%d,",1,%d0
+
+	.data
+0:	.byte	'l','s','x','p','w','d','b','p'
+	.previous
+#endif
+
+	| encode addressing mode for dest
+	fp_decode_addr_mode
+
+	.long	fp_data, fp_ill
+	.long	fp_indirect, fp_postinc
+	.long	fp_predecr, fp_disp16
+	.long	fp_extmode0, fp_extmode1
+
+	| addressing mode: data register direct
+fp_data:
+	fp_mode_data_direct
+	move.w	%d0,%d1
+	fp_decode_src_reg
+	fp_get_fp_reg
+	lea	(FPD_TEMPFP1,FPDATA),%a1
+	move.l	(%a0)+,(%a1)+
+	move.l	(%a0)+,(%a1)+
+	move.l	(%a0),(%a1)
+	lea	(-8,%a1),%a0
+	swap	%d1
+	move.l	%d1,%d2
+	printf	PDECODE,"\n"
+	jmp	([0f:w,%pc,%d1.w*4])
+
+	.align	4
+0:
+	.long	fp_data_long, fp_data_single
+	.long	fp_ill, fp_ill
+	.long	fp_data_word, fp_ill
+	.long	fp_data_byte, fp_ill
+
+fp_data_byte:
+	jsr	fp_normalize_ext
+	jsr	fp_conv_ext2byte
+	move.l	%d0,%d1
+	swap	%d2
+	move.w	%d2,%d0
+	jsr	fp_get_data_reg
+	move.b	%d1,%d0
+	move.w	%d2,%d1
+	jsr	fp_put_data_reg
+	jra	fp_final
+
+fp_data_word:
+	jsr	fp_normalize_ext
+	jsr	fp_conv_ext2short
+	move.l	%d0,%d1
+	swap	%d2
+	move.w	%d2,%d0
+	jsr	fp_get_data_reg
+	move.w	%d1,%d0
+	move.l	%d2,%d1
+	jsr	fp_put_data_reg
+	jra	fp_final
+
+fp_data_long:
+	jsr	fp_normalize_ext
+	jsr	fp_conv_ext2long
+	swap	%d2
+	move.w	%d2,%d1
+	jsr	fp_put_data_reg
+	jra	fp_final
+
+fp_data_single:
+	jsr	fp_normalize_ext
+	jsr	fp_conv_ext2single
+	swap	%d2
+	move.w	%d2,%d1
+	jsr	fp_put_data_reg
+	jra	fp_final
+
+	| addressing mode: address register indirect
+fp_indirect:
+	fp_mode_addr_indirect
+	jra	fp_putdest
+
+	| addressing mode: address register indirect with postincrement
+fp_postinc:
+	fp_mode_addr_indirect_postinc
+	jra	fp_putdest
+
+	| addressing mode: address register indirect with predecrement
+fp_predecr:
+	fp_mode_addr_indirect_predec
+	jra	fp_putdest
+
+	| addressing mode: address register indirect with 16bit displacement
+fp_disp16:
+	fp_mode_addr_indirect_disp16
+	jra     fp_putdest
+
+fp_extmode0:
+	fp_mode_addr_indirect_extmode0
+	jra	fp_putdest
+
+fp_extmode1:
+	fp_decode_addr_reg
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fp_abs_short, fp_abs_long
+	.long	fp_ill, fp_ill
+	.long	fp_ill, fp_ill
+	.long	fp_ill, fp_ill
+
+fp_abs_short:
+	fp_mode_abs_short
+	jra	fp_putdest
+
+fp_abs_long:
+	fp_mode_abs_long
+	jra	fp_putdest
+
+fp_putdest:
+	move.l	%a0,%a1
+	fp_decode_src_reg
+	move.l	%d1,%d2			| save size
+	fp_get_fp_reg
+	printf	PDECODE,"\n"
+	addq.l	#8,%a0
+	move.l	(%a0),-(%sp)
+	move.l	-(%a0),-(%sp)
+	move.l	-(%a0),-(%sp)
+	move.l	%sp,%a0
+	jsr	fp_normalize_ext
+
+	swap	%d2
+	jmp	([0f:w,%pc,%d2.w*4])
+
+	.align	4
+0:
+	.long	fp_format_long, fp_format_single
+	.long	fp_format_extended, fp_format_packed
+	.long	fp_format_word, fp_format_double
+	.long	fp_format_byte, fp_format_packed
+
+fp_format_long:
+	jsr	fp_conv_ext2long
+	putuser.l %d0,(%a1),fp_err_ua1,%a1
+	jra	fp_finish_move
+
+fp_format_single:
+	jsr	fp_conv_ext2single
+	putuser.l %d0,(%a1),fp_err_ua1,%a1
+	jra	fp_finish_move
+
+fp_format_extended:
+	move.l	(%a0)+,%d0
+	lsl.w	#1,%d0
+	lsl.l	#7,%d0
+	lsl.l	#8,%d0
+	putuser.l %d0,(%a1)+,fp_err_ua1,%a1
+	move.l	(%a0)+,%d0
+	putuser.l %d0,(%a1)+,fp_err_ua1,%a1
+	move.l	(%a0),%d0
+	putuser.l %d0,(%a1),fp_err_ua1,%a1
+	jra	fp_finish_move
+
+fp_format_packed:
+	/* not supported yet */
+	lea	(12,%sp),%sp
+	jra	fp_ill
+
+fp_format_word:
+	jsr	fp_conv_ext2short
+	putuser.w %d0,(%a1),fp_err_ua1,%a1
+	jra	fp_finish_move
+
+fp_format_double:
+	jsr	fp_conv_ext2double
+	jra	fp_finish_move
+
+fp_format_byte:
+	jsr	fp_conv_ext2byte
+	putuser.b %d0,(%a1),fp_err_ua1,%a1
+|	jra	fp_finish_move
+
+fp_finish_move:
+	lea	(12,%sp),%sp
+	jra	fp_final
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_movem.S linux/arch/m68k/math-emu/fp_movem.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_movem.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_movem.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,368 @@
+/*
+ * fp_movem.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fp_emu.h"
+#include "fp_decode.h"
+
+| set flags for decode macros for fmovem
+do_fmovem=1
+
+	.globl	fp_fmovem_fp, fp_fmovem_cr
+
+| %d1 contains the mask and count of the register list
+| for other register usage see fp_decode.h
+
+fp_fmovem_fp:
+	printf	PDECODE,"fmovem.x "
+	| get register list and count them
+	btst	#11,%d2
+	jne	1f
+	bfextu	%d2{#24,#8},%d0		| static register list
+	jra	2f
+1:	bfextu	%d2{#25,#3},%d0		| dynamic register list
+	jsr	fp_get_data_reg
+2:	move.l	%d0,%d1
+	swap	%d1
+	jra	2f
+1:	addq.w	#1,%d1			| count the # of registers in
+2:	lsr.b	#1,%d0			| register list and keep it in %d1
+	jcs	1b
+	jne	2b
+	printf	PDECODE,"#%08x",1,%d1
+#ifdef FPU_EMU_DEBUG
+	btst	#12,%d2
+	jne	1f
+	printf	PDECODE,"-"		| decremental move
+	jra	2f
+1:	printf	PDECODE,"+"		| incremental move
+2:	btst	#13,%d2
+	jeq	1f
+	printf	PDECODE,"->"		| fpu -> cpu
+	jra	2f
+1:	printf	PDECODE,"<-"		| fpu <- cpu
+2:
+#endif
+
+	| decode address mode
+	fp_decode_addr_mode
+
+	.long	fp_ill, fp_ill
+	.long	fpr_indirect, fpr_postinc
+	.long	fpr_predecr, fpr_disp16
+	.long	fpr_extmode0, fpr_extmode1
+
+	| addressing mode: address register indirect
+fpr_indirect:
+	fp_mode_addr_indirect
+	jra	fpr_do_movem
+
+	| addressing mode: address register indirect with postincrement
+fpr_postinc:
+	fp_mode_addr_indirect_postinc
+	jra	fpr_do_movem
+
+fpr_predecr:
+	fp_mode_addr_indirect_predec
+	jra	fpr_do_movem
+
+	| addressing mode: address register/programm counter indirect
+	|		   with 16bit displacement
+fpr_disp16:
+	fp_mode_addr_indirect_disp16
+	jra	fpr_do_movem
+
+fpr_extmode0:
+	fp_mode_addr_indirect_extmode0
+	jra	fpr_do_movem
+
+fpr_extmode1:
+	fp_decode_addr_reg
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fpr_absolute_short, fpr_absolute_long
+	.long	fpr_disp16, fpr_extmode0
+	.long	fp_ill, fp_ill
+	.long	fp_ill, fp_ill
+
+fpr_absolute_short:
+	fp_mode_abs_short
+	jra	fpr_do_movem
+
+fpr_absolute_long:
+	fp_mode_abs_long
+|	jra	fpr_do_movem
+
+fpr_do_movem:
+	swap	%d1			| get fpu register list
+	lea	(FPD_FPREG,FPDATA),%a1
+	moveq	#12,%d0
+	btst	#12,%d2
+	jne	1f
+	lea	(-12,%a1,%d0*8),%a1
+	neg.l	%d0
+1:	btst	#13,%d2
+	jne	4f
+	| move register from memory into fpu
+	jra	3f
+1:	printf	PMOVEM,"(%p>%p)",2,%a0,%a1
+	getuser.l (%a0)+,%d2,fp_err_ua1,%a0
+	lsr.l	#8,%d2
+	lsr.l	#7,%d2
+	lsr.w	#1,%d2
+	move.l	%d2,(%a1)+
+	getuser.l (%a0)+,%d2,fp_err_ua1,%a0
+	move.l	%d2,(%a1)+
+	getuser.l (%a0),%d2,fp_err_ua1,%a0
+	move.l	%d2,(%a1)
+	subq.l	#8,%a0
+	subq.l	#8,%a1
+	add.l	%d0,%a0
+2:	add.l	%d0,%a1
+3:	lsl.b	#1,%d1
+	jcs	1b
+	jne	2b
+	jra	5f
+	| move register from fpu into memory
+1:	printf	PMOVEM,"(%p>%p)",2,%a1,%a0
+	move.l	(%a1)+,%d2
+	lsl.w	#1,%d2
+	lsl.l	#7,%d2
+	lsl.l	#8,%d2
+	putuser.l %d2,(%a0)+,fp_err_ua1,%a0
+	move.l	(%a1)+,%d2
+	putuser.l %d2,(%a0)+,fp_err_ua1,%a0
+	move.l	(%a1),%d2
+	putuser.l %d2,(%a0),fp_err_ua1,%a0
+	subq.l	#8,%a1
+	subq.l	#8,%a0
+	add.l	%d0,%a0
+2:	add.l	%d0,%a1
+4:	lsl.b	#1,%d1
+	jcs	1b
+	jne	2b
+5:
+	printf	PDECODE,"\n"
+#if 0
+	lea	(FPD_FPREG,FPDATA),%a0
+	printf	PMOVEM,"fp:"
+	printx	PMOVEM,%a0@(0)
+	printx	PMOVEM,%a0@(12)
+	printf	PMOVEM,"\n   "
+	printx	PMOVEM,%a0@(24)
+	printx	PMOVEM,%a0@(36)
+	printf	PMOVEM,"\n   "
+	printx	PMOVEM,%a0@(48)
+	printx	PMOVEM,%a0@(60)
+	printf	PMOVEM,"\n   "
+	printx	PMOVEM,%a0@(72)
+	printx	PMOVEM,%a0@(84)
+	printf	PMOVEM,"\n"
+#endif
+	jra	fp_end
+
+| set flags for decode macros for fmovem control register
+do_fmovem=1
+do_fmovem_cr=1
+
+fp_fmovem_cr:
+	printf	PDECODE,"fmovem.cr "
+	| get register list and count them
+	bfextu	%d2{#19,#3},%d0
+	move.l	%d0,%d1
+	swap	%d1
+	jra	2f
+1:	addq.w	#1,%d1
+2:	lsr.l	#1,%d0
+	jcs	1b
+	jne	2b
+	printf	PDECODE,"#%08x",1,%d1
+#ifdef FPU_EMU_DEBUG
+	btst	#13,%d2
+	jeq	1f
+	printf	PDECODE,"->"		| fpu -> cpu
+	jra	2f
+1:	printf	PDECODE,"<-"		| fpu <- cpu
+2:
+#endif
+
+	| decode address mode
+	fp_decode_addr_mode
+
+	.long	fpc_data, fpc_addr
+	.long	fpc_indirect, fpc_postinc
+	.long	fpc_predecr, fpc_disp16
+	.long	fpc_extmode0, fpc_extmode1
+
+fpc_data:
+	fp_mode_data_direct
+	move.w	%d0,%d1
+	bfffo	%d2{#19,#3},%d0
+	sub.w	#19,%d0
+	lea	(FPD_FPCR,FPDATA,%d0.w*4),%a1
+	btst	#13,%d2
+	jne	1f
+	move.w	%d1,%d0
+	jsr	fp_get_data_reg
+	move.l	%d0,(%a1)
+	jra	fpc_movem_fin
+1:	move.l	(%a1),%d0
+	jsr	fp_put_data_reg
+	jra	fpc_movem_fin
+
+fpc_addr:
+	fp_decode_addr_reg
+	printf	PDECODE,"a%d",1,%d0
+	btst	#13,%d2
+	jne	1f
+	jsr	fp_get_addr_reg
+	move.l	%a0,(FPD_FPIAR,FPDATA)
+	jra	fpc_movem_fin
+1:	move.l	(FPD_FPIAR,FPDATA),%a0
+	jsr	fp_put_addr_reg
+	jra	fpc_movem_fin
+
+fpc_indirect:
+	fp_mode_addr_indirect
+	jra	fpc_do_movem
+
+fpc_postinc:
+	fp_mode_addr_indirect_postinc
+	jra	fpc_do_movem
+
+fpc_predecr:
+	fp_mode_addr_indirect_predec
+	jra	fpc_do_movem
+
+fpc_disp16:
+	fp_mode_addr_indirect_disp16
+	jra	fpc_do_movem
+
+fpc_extmode0:
+	fp_mode_addr_indirect_extmode0
+	jra	fpc_do_movem
+
+fpc_extmode1:
+	fp_decode_addr_reg
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fpc_absolute_short, fpc_absolute_long
+	.long	fpc_disp16, fpc_extmode0
+	.long	fpc_immediate, fp_ill
+	.long	fp_ill, fp_ill
+
+fpc_absolute_short:
+	fp_mode_abs_short
+	jra	fpc_do_movem
+
+fpc_absolute_long:
+	fp_mode_abs_long
+	jra	fpc_do_movem
+
+fpc_immediate:
+	fp_get_pc %a0
+	lea	(%a0,%d1.w*4),%a1
+	fp_put_pc %a1
+	printf	PDECODE,"#imm"
+|	jra	fpc_do_movem
+#if 0
+	swap	%d1
+	lsl.l	#5,%d1
+	lea	(FPD_FPCR,FPDATA),%a0
+	jra	3f
+1:	move.l	%d0,(%a0)
+2:	addq.l	#4,%a0
+3:	lsl.b	#1,%d1
+	jcs	1b
+	jne	2b
+	jra	fpc_movem_fin
+#endif
+
+fpc_do_movem:
+	swap	%d1			| get fpu register list
+	lsl.l	#5,%d1
+	lea	(FPD_FPCR,FPDATA),%a1
+1:	btst	#13,%d2
+	jne	4f
+
+	| move register from memory into fpu
+	jra	3f
+1:	printf	PMOVEM,"(%p>%p)",2,%a0,%a1
+	getuser.l (%a0)+,%d0,fp_err_ua1,%a0
+	move.l	%d0,(%a1)
+2:	addq.l	#4,%a1
+3:	lsl.b	#1,%d1
+	jcs	1b
+	jne	2b
+	jra	fpc_movem_fin
+
+	| move register from fpu into memory
+1:	printf	PMOVEM,"(%p>%p)",2,%a1,%a0
+	move.l	(%a1),%d0
+	putuser.l %d0,(%a0)+,fp_err_ua1,%a0
+2:	addq.l	#4,%a1
+4:	lsl.b	#1,%d1
+	jcs	1b
+	jne	2b
+
+fpc_movem_fin:
+	and.l	#0x0000fff0,(FPD_FPCR,FPDATA)
+	and.l	#0x0ffffff8,(FPD_FPSR,FPDATA)
+	move.l	(FPD_FPCR,FPDATA),%d0
+	lsr.l	#4,%d0
+	moveq	#3,%d1
+	and.l	%d0,%d1
+	move.w	%d1,(FPD_RND,FPDATA)
+	lsr.l	#2,%d0
+	moveq	#3,%d1
+	and.l	%d0,%d1
+	move.w	%d1,(FPD_PREC,FPDATA)
+	printf	PDECODE,"\n"
+#if 0
+	printf	PMOVEM,"fpcr : %08x\n",1,FPDATA@(FPD_FPCR)
+	printf	PMOVEM,"fpsr : %08x\n",1,FPDATA@(FPD_FPSR)
+	printf	PMOVEM,"fpiar: %08x\n",1,FPDATA@(FPD_FPIAR)
+	clr.l	%d0
+	move.w	(FPD_PREC,FPDATA),%d0
+	printf	PMOVEM,"prec : %04x\n",1,%d0
+	move.w	(FPD_RND,FPDATA),%d0
+	printf	PMOVEM,"rnd  : %04x\n",1,%d0
+#endif
+	jra	fp_end
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_scan.S linux/arch/m68k/math-emu/fp_scan.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_scan.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_scan.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,478 @@
+/*
+ * fp_scan.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fp_emu.h"
+#include "fp_decode.h"
+
+	.globl	fp_scan, fp_datasize
+
+	.data
+
+| %d2 - first two instr words
+| %d1 - operand size
+
+/* operand formats are:
+
+	Long = 0,		i.e. fmove.l
+	Single,			i.e. fmove.s
+	Extended,		i.e. fmove.x
+	Packed-BCD,		i.e. fmove.p
+	Word,			i.e. fmove.w
+	Double,			i.e. fmove.d
+*/
+
+	.text
+
+| On entry:
+| FPDATA - base of emulated FPU registers
+
+fp_scan:
+| normal fpu instruction? (this excludes fsave/frestore)
+	fp_get_pc %a0
+	printf	PDECODE,"%08x: ",1,%a0
+	getuser.b (%a0),%d0,fp_err_ua1,%a0
+#if 1
+	cmp.b	#0xf2,%d0		| cpid = 1
+#else
+	cmp.b	#0xfc,%d0		| cpid = 6
+#endif
+	jne	fp_nonstd
+| first two instruction words are kept in %d2
+	getuser.l (%a0)+,%d2,fp_err_ua1,%a0
+	fp_put_pc %a0
+fp_decode_cond:				| seperate conditional instr
+	fp_decode_cond_instr_type
+
+	.long	fp_decode_move, fp_fscc
+	.long	fp_fbccw, fp_fbccl
+
+fp_decode_move:				| seperate move instr
+	fp_decode_move_instr_type
+
+	.long	fp_fgen_fp, fp_ill
+	.long	fp_fgen_ea, fp_fmove_fp2mem
+	.long	fp_fmovem_cr, fp_fmovem_cr
+	.long	fp_fmovem_fp, fp_fmovem_fp
+
+| now all arithmetic instr and a few move instr are left
+fp_fgen_fp:				| source is a fpu register
+	clr.b	(FPD_FPSR+2,FPDATA)	| clear the exception byte
+	fp_decode_sourcespec
+	printf	PDECODE,"f<op>.x fp%d",1,%d0
+	fp_get_fp_reg
+	lea	(FPD_TEMPFP1,FPDATA),%a1 | copy src into a temp location
+	move.l	(%a0)+,(%a1)+
+	move.l	(%a0)+,(%a1)+
+	move.l	(%a0),(%a1)
+	lea	(-8,%a1),%a0
+	jra	fp_getdest
+
+fp_fgen_ea:				| source is <ea>
+	clr.b	(FPD_FPSR+2,FPDATA)	| clear the exception byte
+	| sort out fmovecr, keep data size in %d1
+	fp_decode_sourcespec
+	cmp.w	#7,%d0
+	jeq	fp_fmovecr
+	move.w	%d0,%d1			| store data size twice in %d1
+	swap	%d1			| one can be trashed below
+	move.w	%d0,%d1
+#ifdef FPU_EMU_DEBUG
+	lea	0f,%a0
+	clr.l	%d0
+	move.b	(%a0,%d1.w),%d0
+	printf	PDECODE,"f<op>.%c ",1,%d0
+
+	.data
+0:	.byte	'l','s','x','p','w','d','b',0
+	.previous
+#endif
+
+/*
+	fp_getsource, fp_getdest
+
+	basically, we end up with a pointer to the source operand in
+	%a1, and a pointer to the destination operand in %a0.  both
+	are, of course, 96-bit extended floating point numbers.
+*/
+
+fp_getsource:
+	| decode addressing mode for source
+	fp_decode_addr_mode
+
+	.long 	fp_data, fp_ill
+	.long 	fp_indirect, fp_postinc
+	.long 	fp_predecr, fp_disp16
+	.long 	fp_extmode0, fp_extmode1
+
+	| addressing mode: data register direct
+fp_data:
+	fp_mode_data_direct
+	jsr	fp_get_data_reg
+	lea	(FPD_TEMPFP1,FPDATA),%a0
+	jmp	([0f:w,%pc,%d1.w*4])
+
+	.align	4
+0:
+	.long	fp_data_long, fp_data_single
+	.long	fp_ill, fp_ill
+	.long	fp_data_word, fp_ill
+	.long	fp_data_byte, fp_ill
+
+	| data types that fit in an integer data register
+fp_data_byte:
+	extb.l	%d0
+	jra	fp_data_long
+
+fp_data_word:
+	ext.l	%d0
+
+fp_data_long:
+	jsr	fp_conv_long2ext
+	jra	fp_getdest
+
+fp_data_single:
+	jsr	fp_conv_single2ext
+	jra	fp_getdest
+
+	| addressing mode: address register indirect
+fp_indirect:
+	fp_mode_addr_indirect
+	jra	fp_fetchsource
+
+	| addressing mode: address register indirect with postincrement
+fp_postinc:
+	fp_mode_addr_indirect_postinc
+	jra	fp_fetchsource
+
+	| addressing mode: address register indirect with predecrement
+fp_predecr:
+	fp_mode_addr_indirect_predec
+	jra	fp_fetchsource
+
+	| addressing mode: address register/programm counter indirect
+	|		   with 16bit displacement
+fp_disp16:
+	fp_mode_addr_indirect_disp16
+	jra	fp_fetchsource
+
+	| all other indirect addressing modes will finally end up here
+fp_extmode0:
+	fp_mode_addr_indirect_extmode0
+	jra	fp_fetchsource
+
+| all pc relative addressing modes and immediate/absolute modes end up here
+| the first ones are sent to fp_extmode0 or fp_disp16
+| and only the latter are handled here
+fp_extmode1:
+	fp_decode_addr_reg
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fp_abs_short, fp_abs_long
+	.long	fp_disp16, fp_extmode0
+	.long	fp_immediate, fp_ill
+	.long	fp_ill, fp_ill
+
+	| addressing mode: absolute short
+fp_abs_short:
+	fp_mode_abs_short
+	jra	fp_fetchsource
+
+	| addressing mode: absolute long
+fp_abs_long:
+	fp_mode_abs_long
+	jra	fp_fetchsource
+
+	| addressing mode: immediate data
+fp_immediate:
+	printf	PDECODE,"#"
+	fp_get_pc %a0
+	move.w	(fp_datasize,%d1.w*2),%d0
+	addq.w	#1,%d0
+	and.w	#-2,%d0
+#ifdef FPU_EMU_DEBUG
+	movem.l	%d0/%d1,-(%sp)
+	movel	%a0,%a1
+	clr.l	%d1
+	jra	2f
+1:	getuser.b (%a1)+,%d1,fp_err_ua1,%a1
+	printf	PDECODE,"%02x",1,%d1
+2:	dbra	%d0,1b
+	movem.l	(%sp)+,%d0/%d1
+#endif
+	lea	(%a0,%d0.w),%a1
+	fp_put_pc %a1
+|	jra	fp_fetchsource
+
+fp_fetchsource:
+	move.l	%a0,%a1
+	swap	%d1
+	lea	(FPD_TEMPFP1,FPDATA),%a0
+	jmp	([0f:w,%pc,%d1.w*4])
+
+	.align	4
+0:	.long	fp_long, fp_single
+	.long	fp_ext, fp_pack
+	.long	fp_word, fp_double
+	.long	fp_byte, fp_ill
+
+fp_long:
+	getuser.l (%a1),%d0,fp_err_ua1,%a1
+	jsr	fp_conv_long2ext
+	jra	fp_getdest
+
+fp_single:
+	getuser.l (%a1),%d0,fp_err_ua1,%a1
+	jsr	fp_conv_single2ext
+	jra	fp_getdest
+
+fp_ext:
+	getuser.l (%a1)+,%d0,fp_err_ua1,%a1
+	lsr.l	#8,%d0
+	lsr.l	#7,%d0
+	lsr.w	#1,%d0
+	move.l	%d0,(%a0)+
+	getuser.l (%a1)+,%d0,fp_err_ua1,%a1
+	move.l	%d0,(%a0)+
+	getuser.l (%a1),%d0,fp_err_ua1,%a1
+	move.l	%d0,(%a0)
+	subq.l	#8,%a0
+	jra	fp_getdest
+
+fp_pack:
+	/* not supported yet */
+	jra	fp_ill
+
+fp_word:
+	getuser.w (%a1),%d0,fp_err_ua1,%a1
+	ext.l	%d0
+	jsr	fp_conv_long2ext
+	jra	fp_getdest
+
+fp_double:
+	jsr	fp_conv_double2ext
+	jra	fp_getdest
+
+fp_byte:
+	getuser.b (%a1),%d0,fp_err_ua1,%a1
+	extb.l	%d0
+	jsr	fp_conv_long2ext
+|	jra	fp_getdest
+
+fp_getdest:
+	move.l	%a0,%a1
+	bfextu	%d2{#22,#3},%d0
+	printf	PDECODE,",fp%d\n",1,%d0
+	fp_get_fp_reg
+	movem.l	%a0/%a1,-(%sp)
+	pea	fp_finalrounding
+	bfextu	%d2{#25,#7},%d0
+	jmp	([0f:w,%pc,%d0*4])
+
+	.align	4
+0:
+	.long	fp_fmove_mem2fp, fp_fint, fp_fsinh, fp_fintrz
+	.long	fp_fsqrt, fp_ill, fp_flognp1, fp_ill
+	.long	fp_fetoxm1, fp_ftanh, fp_fatan, fp_ill
+	.long	fp_fasin, fp_fatanh, fp_fsin, fp_ftan
+	.long	fp_fetox, fp_ftwotox, fp_ftentox, fp_ill
+	.long	fp_flogn, fp_flog10, fp_flog2, fp_ill
+	.long	fp_fabs, fp_fcosh, fp_fneg, fp_ill
+	.long	fp_facos, fp_fcos, fp_fgetexp, fp_fgetman
+	.long	fp_fdiv, fp_fmod, fp_fadd, fp_fmul
+	.long	fpa_fsgldiv, fp_frem, fp_fscale, fpa_fsglmul
+	.long	fp_fsub, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_fsincos0, fp_fsincos1, fp_fsincos2, fp_fsincos3
+	.long	fp_fsincos4, fp_fsincos5, fp_fsincos6, fp_fsincos7
+	.long	fp_fcmp, fp_ill, fp_ftst, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_fsmove, fp_fssqrt, fp_ill, fp_ill
+	.long	fp_fdmove, fp_fdsqrt, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_fsabs, fp_ill, fp_fsneg, fp_ill
+	.long	fp_fdabs, fp_ill, fp_fdneg, fp_ill
+	.long	fp_fsdiv, fp_ill, fp_fsadd, fp_fsmul
+	.long	fp_fddiv, fp_ill, fp_fdadd, fp_fdmul
+	.long	fp_fssub, fp_ill, fp_ill, fp_ill
+	.long	fp_fdsub, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+	.long	fp_ill, fp_ill, fp_ill, fp_ill
+
+	| Instructions follow
+
+	| Move an (emulated) ROM constant
+fp_fmovecr:
+	bfextu	%d2{#27,#5},%d0
+	printf	PINSTR,"fp_fmovecr #%d",1,%d0
+	move.l	%d0,%d1
+	add.l	%d0,%d0
+	add.l	%d1,%d0
+	lea	(fp_constants,%d0*4),%a0
+	move.l	#0x801cc0ff,%d0
+	addq.l	#1,%d1
+	lsl.l	%d1,%d0
+	jcc	1f
+	fp_set_sr FPSR_EXC_INEX2			| INEX2 exception
+1:	moveq	#-128,%d0				| continue with fmove
+	and.l	%d0,%d2
+	jra	fp_getdest
+
+	.data
+	.align	4
+fp_constants:
+	.long	0x00004000,0xc90fdaa2,0x2168c235	| pi
+	.extend	0,0,0,0,0,0,0,0,0,0
+	.long	0x00003ffd,0x9a209a84,0xfbcff798	| log10(2)
+	.long	0x00004000,0xadf85458,0xa2bb4a9a	| e
+	.long	0x00003fff,0xb8aa3b29,0x5c17f0bc	| log2(e)
+	.long	0x00003ffd,0xde5bd8a9,0x37287195	| log10(e)
+	.long	0x00000000,0x00000000,0x00000000	| 0.0
+	.long	0x00003ffe,0xb17217f7,0xd1cf79ac	| 1n(2)
+	.long	0x00004000,0x935d8ddd,0xaaa8ac17	| 1n(10)
+	| read this as "1.0 * 2^0" - note the high bit in the mantissa
+	.long	0x00003fff,0x80000000,0x00000000	| 10^0
+	.long	0x00004002,0xa0000000,0x00000000	| 10^1
+	.long	0x00004005,0xc8000000,0x00000000	| 10^2
+	.long	0x0000400c,0x9c400000,0x00000000	| 10^4
+	.long	0x00004019,0xbebc2000,0x00000000	| 10^8
+	.long	0x00004034,0x8e1bc9bf,0x04000000	| 10^16
+	.long	0x00004069,0x9dc5ada8,0x2b70b59e	| 10^32
+	.long	0x000040d3,0xc2781f49,0xffcfa6d5	| 10^64
+	.long	0x000041a8,0x93ba47c9,0x80e98ce0	| 10^128
+	.long	0x00004351,0xaa7eebfb,0x9df9de8e	| 10^256
+	.long	0x000046a3,0xe319a0ae,0xa60e91c7	| 10^512
+	.long	0x00004d48,0xc9767586,0x81750c17	| 10^1024
+	.long	0x00005a92,0x9e8b3b5d,0xc53d5de5	| 10^2048
+	.long	0x00007525,0xc4605202,0x8a20979b	| 10^4096
+	.previous
+
+fp_fmove_mem2fp:
+	printf	PINSTR,"fmove %p,%p\n",2,%a0,%a1
+	move.l	(%a1)+,(%a0)+
+	move.l	(%a1)+,(%a0)+
+	move.l	(%a1),(%a0)
+	subq.l	#8,%a0
+	rts
+
+fpa_fsglmul:
+	move.l	#fp_finalrounding_single_fast,(%sp)
+	jra	fp_fsglmul
+
+fpa_fsgldiv:
+	move.l	#fp_finalrounding_single_fast,(%sp)
+	jra	fp_fsgldiv
+
+.macro	fp_dosingleprec instr
+	printf	PINSTR,"single "
+	move.l	#fp_finalrounding_single,(%sp)
+	jra	\instr
+.endm
+
+.macro	fp_dodoubleprec instr
+	printf	PINSTR,"double "
+	move.l	#fp_finalrounding_double,(%sp)
+	jra	\instr
+.endm
+
+fp_fsmove:
+	fp_dosingleprec fp_fmove_mem2fp
+
+fp_fssqrt:
+	fp_dosingleprec fp_fsqrt
+
+fp_fdmove:
+	fp_dodoubleprec fp_fmove_mem2fp
+
+fp_fdsqrt:
+	fp_dodoubleprec fp_fsqrt
+
+fp_fsabs:
+	fp_dosingleprec fp_fabs
+
+fp_fsneg:
+	fp_dosingleprec fp_fneg
+
+fp_fdabs:
+	fp_dodoubleprec fp_fabs
+
+fp_fdneg:
+	fp_dodoubleprec fp_fneg
+
+fp_fsdiv:
+	fp_dosingleprec fp_fdiv
+
+fp_fsadd:
+	fp_dosingleprec fp_fadd
+
+fp_fsmul:
+	fp_dosingleprec fp_fmul
+
+fp_fddiv:
+	fp_dodoubleprec fp_fdiv
+
+fp_fdadd:
+	fp_dodoubleprec fp_fadd
+
+fp_fdmul:
+	fp_dodoubleprec fp_fmul
+
+fp_fssub:
+	fp_dosingleprec fp_fsub
+
+fp_fdsub:
+	fp_dodoubleprec fp_fsub
+
+fp_nonstd:
+	fp_get_pc %a0
+	getuser.l (%a0),%d0,fp_err_ua1,%a0
+	printf	,"nonstd ((%08x)=%08x)\n",2,%a0,%d0
+	moveq	#-1,%d0
+	rts
+
+	.data
+	.align	4
+
+	| data sizes corresponding to the operand formats
+fp_datasize:
+	.word	4, 4, 12, 12, 2, 8, 1, 0
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_trig.c linux/arch/m68k/math-emu/fp_trig.c
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_trig.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_trig.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,183 @@
+/*
+
+  fp_trig.c: floating-point math routines for the Linux-m68k
+  floating point emulator.
+
+  Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel.
+
+  I hereby give permission, free of charge, to copy, modify, and
+  redistribute this software, in source or binary form, provided that
+  the above copyright notice and the following disclaimer are included
+  in all such copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL
+  OR IMPLIED.
+
+*/
+
+#include "fp_emu.h"
+#include "fp_trig.h"
+
+struct fp_ext *
+fp_fsin(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsin\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fcos(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fcos\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_ftan(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("ftan\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fasin(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fasin\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_facos(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("facos\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fatan(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fatan\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsinh(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsinh\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fcosh(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fcosh\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_ftanh(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("ftanh\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fatanh(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fatanh\n");
+
+	fp_monadic_check(dest, src);
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos0(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos0\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos1(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos1\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos2(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos2\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos3(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos3\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos4(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos4\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos5(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos5\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos6(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos6\n");
+
+	return dest;
+}
+
+struct fp_ext *
+fp_fsincos7(struct fp_ext *dest, struct fp_ext *src)
+{
+	uprint("fsincos7\n");
+
+	return dest;
+}
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_trig.h linux/arch/m68k/math-emu/fp_trig.h
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_trig.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_trig.h	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,32 @@
+/*
+
+  fp_trig.h: floating-point math routines for the Linux-m68k
+  floating point emulator.
+
+  Copyright (c) 1998 David Huggins-Daines.
+
+  I hereby give permission, free of charge, to copy, modify, and
+  redistribute this software, in source or binary form, provided that
+  the above copyright notice and the following disclaimer are included
+  in all such copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL
+  OR IMPLIED.
+
+*/
+
+#ifndef FP_TRIG_H
+#define FP_TRIG_H
+
+#include "fp_emu.h"
+
+/* floating point trigonometric instructions:
+
+   the arguments to these are in the "internal" extended format, that
+   is, an "exploded" version of the 96-bit extended fp format used by
+   the 68881.
+
+   they return a status code, which should end up in %d0, if all goes
+   well.  */
+
+#endif /* FP_TRIG__H */
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/fp_util.S linux/arch/m68k/math-emu/fp_util.S
--- linux-2.2.18-pre15/arch/m68k/math-emu/fp_util.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/fp_util.S	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,1454 @@
+/*
+ * fp_util.S
+ *
+ * Copyright Roman Zippel, 1997.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fp_emu.h"
+
+/*
+ * Here are lots of conversion and normalization functions mainly
+ * used by fp_scan.S
+ * Note that these functions are optimized for "normal" numbers,
+ * these are handled first and exit as fast as possible, this is
+ * especially important for fp_normalize_ext/fp_conv_ext2ext, as
+ * it's called very often.
+ * The register usage is optimized for fp_scan.S and which register
+ * is currently at that time unused, be careful if you want change
+ * something here. %d0 and %d1 is always usable, sometimes %d2 (or
+ * only the lower half) most function have to return the %a0
+ * unmodified, so that the caller can immediatly reuse it.
+ */
+
+	.globl	fp_ill, fp_end
+
+	| exits from fp_scan:
+	| illegal instruction
+fp_ill:
+	printf	,"fp_illegal\n"
+	rts
+	| completed instruction
+fp_end:
+	tst.l	(TASK_MM-8,%a2)
+	jmi	1f
+	tst.l	(TASK_MM-4,%a2)
+	jmi	1f
+	tst.l	(TASK_MM,%a2)
+	jpl	2f
+1:	printf	,"oops:%p,%p,%p\n",3,%a2@(TASK_MM-8),%a2@(TASK_MM-4),%a2@(TASK_MM)
+2:	clr.l	%d0
+	rts
+
+	.globl	fp_conv_long2ext, fp_conv_single2ext
+	.globl	fp_conv_double2ext, fp_conv_ext2ext
+	.globl	fp_normalize_ext, fp_normalize_double
+	.globl	fp_normalize_single, fp_normalize_single_fast
+	.globl	fp_conv_ext2double, fp_conv_ext2single
+	.globl	fp_conv_ext2long, fp_conv_ext2short
+	.globl	fp_conv_ext2byte
+	.globl	fp_finalrounding_single, fp_finalrounding_single_fast
+	.globl	fp_finalrounding_double
+	.globl	fp_finalrounding, fp_finaltest, fp_final
+
+/*
+ * First several conversion functions from a source operand
+ * into the extended format. Note, that only fp_conv_ext2ext
+ * normalizes the number and is always called after the other
+ * conversion functions, which only move the information into
+ * fp_ext structure.
+ */
+
+	| fp_conv_long2ext:
+	|
+	| args:	%d0 = source (32-bit long)
+	|	%a0 = destination (ptr to struct fp_ext)
+
+fp_conv_long2ext:
+	printf	PCONV,"l2e: %p -> %p(",2,%d0,%a0
+	clr.l	%d1			| sign defaults to zero
+	tst.l	%d0
+	jeq	fp_l2e_zero		| is source zero?
+	jpl	1f			| positive?
+	moveq	#1,%d1
+	neg.l	%d0
+1:	swap	%d1
+	move.w	#0x3fff+31,%d1
+	move.l	%d1,(%a0)+		| set sign / exp
+	move.l	%d0,(%a0)+		| set mantissa
+	clr.l	(%a0)
+	subq.l	#8,%a0			| restore %a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	rts
+	| source is zero
+fp_l2e_zero:
+	clr.l	(%a0)+
+	clr.l	(%a0)+
+	clr.l	(%a0)
+	subq.l	#8,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	rts
+
+	| fp_conv_single2ext
+	| args:	%d0 = source (single-precision fp value)
+	|	%a0 = dest (struct fp_ext *)
+
+fp_conv_single2ext:
+	printf	PCONV,"s2e: %p -> %p(",2,%d0,%a0
+	move.l	%d0,%d1
+	lsl.l	#8,%d0			| shift mantissa
+	lsr.l	#8,%d1			| exponent / sign
+	lsr.l	#7,%d1
+	lsr.w	#8,%d1
+	jeq	fp_s2e_small		| zero / denormal?
+	cmp.w	#0xff,%d1		| NaN / Inf?
+	jeq	fp_s2e_large
+	bset	#31,%d0			| set explizit bit
+	add.w	#0x3fff-0x7f,%d1	| re-bias the exponent.
+9:	move.l	%d1,(%a0)+		| fp_ext.sign, fp_ext.exp
+	move.l	%d0,(%a0)+		| high lword of fp_ext.mant
+	clr.l	(%a0)			| low lword = 0
+	subq.l	#8,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	rts
+	| zeros and denormalized
+fp_s2e_small:
+	| exponent is zero, so explizit bit is already zero too
+	tst.l	%d0
+	jeq	9b
+	move.w	#0x4000-0x7f,%d1
+	jra	9b
+	| infinities and NAN
+fp_s2e_large:
+	bclr	#31,%d0			| clear explizit bit
+	move.w	#0x7fff,%d1
+	jra	9b
+
+fp_conv_double2ext:
+#ifdef FPU_EMU_DEBUG
+	getuser.l %a1@(0),%d0,fp_err_ua2,%a1
+	getuser.l %a1@(4),%d1,fp_err_ua2,%a1
+	printf	PCONV,"d2e: %p%p -> %p(",3,%d0,%d1,%a0
+#endif
+	getuser.l (%a1)+,%d0,fp_err_ua2,%a1
+	move.l	%d0,%d1
+	lsl.l	#8,%d0			| shift high mantissa
+	lsl.l	#3,%d0
+	lsr.l	#8,%d1			| exponent / sign
+	lsr.l	#7,%d1
+	lsr.w	#5,%d1
+	jeq	fp_d2e_small		| zero / denormal?
+	cmp.w	#0x7ff,%d1		| NaN / Inf?
+	jeq	fp_d2e_large
+	bset	#31,%d0			| set explizit bit
+	add.w	#0x3fff-0x3ff,%d1	| re-bias the exponent.
+9:	move.l	%d1,(%a0)+		| fp_ext.sign, fp_ext.exp
+	move.l	%d0,(%a0)+
+	getuser.l (%a1)+,%d0,fp_err_ua2,%a1
+	move.l	%d0,%d1
+	lsl.l	#8,%d0
+	lsl.l	#3,%d0
+	move.l	%d0,(%a0)
+	moveq	#21,%d0
+	lsr.l	%d0,%d1
+	or.l	%d1,-(%a0)
+	subq.l	#4,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	rts
+	| zeros and denormalized
+fp_d2e_small:
+	| exponent is zero, so explizit bit is already zero too
+	tst.l	%d0
+	jeq	9b
+	move.w	#0x4000-0x3ff,%d1
+	jra	9b
+	| infinities and NAN
+fp_d2e_large:
+	bclr	#31,%d0			| clear explizit bit
+	move.w	#0x7fff,%d1
+	jra	9b
+
+	| fp_conv_ext2ext:
+	| originally used to get longdouble from userspace, now it's
+	| called before arithmetic operations to make sure the number
+	| is normalized [maybe rename it?].
+	| args:	%a0 = dest (struct fp_ext *)
+	| returns 0 in %d0 for a NaN, otherwise 1
+
+fp_conv_ext2ext:
+	printf	PCONV,"e2e: %p(",1,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,"), "
+	move.l	(%a0)+,%d0
+	cmp.w	#0x7fff,%d0		| Inf / NaN?
+	jeq	fp_e2e_large
+	move.l	(%a0),%d0
+	jpl	fp_e2e_small		| zero / denorm?
+	| The high bit is set, so normalization is irrelevant.
+fp_e2e_checkround:
+	subq.l	#4,%a0
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	move.b	(%a0),%d0
+	jne	fp_e2e_round
+#endif
+	printf	PCONV,"%p(",1,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	moveq	#1,%d0
+	rts
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+fp_e2e_round:
+	fp_set_sr FPSR_EXC_INEX2
+	clr.b	(%a0)
+	move.w	(FPD_RND,FPDATA),%d2
+	jne	fp_e2e_roundother	| %d2 == 0, round to nearest
+	tst.b	%d0			| test guard bit
+	jpl	9f			| zero is closer
+	btst	#0,(11,%a0)		| test lsb bit
+	jne	fp_e2e_doroundup	| round to infinity
+	lsl.b	#1,%d0			| check low bits
+	jeq	9f			| round to zero
+fp_e2e_doroundup:
+	addq.l	#1,(8,%a0)
+	jcc	9f
+	addq.l	#1,(4,%a0)
+	jcc	9f
+	move.w	#0x8000,(4,%a0)
+	addq.w	#1,(2,%a0)
+9:	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+fp_e2e_roundother:
+	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	1f			| %d2 > 2, round to +infinity
+	tst.b	(1,%a0)			| to -inf
+	jne	fp_e2e_doroundup	| negative, round to infinity
+	jra	9b			| positive, round to zero
+1:	tst.b	(1,%a0)			| to +inf
+	jeq	fp_e2e_doroundup	| positive, round to infinity
+	jra	9b			| negative, round to zero
+#endif
+	| zeros and subnormals:
+	| try to normalize these anyway.
+fp_e2e_small:
+	jne	fp_e2e_small1		| high lword zero?
+	move.l	(4,%a0),%d0
+	jne	fp_e2e_small2
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	jne	fp_e2e_small3
+#endif
+	| Genuine zero.
+	clr.w	-(%a0)
+	subq.l	#2,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	moveq	#1,%d0
+	rts
+	| definitely subnormal, need to shift all 64 bits
+fp_e2e_small1:
+	bfffo	%d0{#0,#32},%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+1:	move.w	%d2,(%a0)+
+	move.w	%d1,%d2
+	jeq	fp_e2e_checkround
+	| fancy 64-bit double-shift begins here
+	lsl.l	%d2,%d0
+	move.l	%d0,(%a0)+
+	move.l	(%a0),%d0
+	move.l	%d0,%d1
+	lsl.l	%d2,%d0
+	move.l	%d0,(%a0)
+	neg.w	%d2
+	and.w	#0x1f,%d2
+	lsr.l	%d2,%d1
+	or.l	%d1,-(%a0)
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+fp_e2e_extra1:
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	neg.w	%d2
+	add.w	#24,%d2
+	jcc	1f
+	clr.b	(-4,%a0)
+	lsl.l	%d2,%d0
+	or.l	%d0,(4,%a0)
+	jra	fp_e2e_checkround
+1:	addq.w	#8,%d2
+	lsl.l	%d2,%d0
+	move.b	%d0,(-4,%a0)
+	lsr.l	#8,%d0
+	or.l	%d0,(4,%a0)
+#endif
+	jra	fp_e2e_checkround
+	| pathologically small subnormal
+fp_e2e_small2:
+	bfffo	%d0{#0,#32},%d1
+	add.w	#32,%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Beyond pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+1:	move.w	%d2,(%a0)+
+	ext.l	%d1
+	jeq	fp_e2e_checkround
+	clr.l	(4,%a0)
+	sub.w	#32,%d2
+	jcs	1f
+	lsl.l	%d1,%d0			| lower lword needs only to be shifted
+	move.l	%d0,(%a0)		| into the higher lword
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	clr.b	(-4,%a0)
+	neg.w	%d1
+	add.w	#32,%d1
+	bfins	%d0,(%a0){%d1,#8}
+#endif
+	jra	fp_e2e_checkround
+1:	neg.w	%d1			| lower lword is splitted between
+	bfins	%d0,(%a0){%d1,#32}	| higher and lower lword
+#ifndef CONFIG_FPU_EMU_EXTRAPREC
+	jra	fp_e2e_checkround
+#else
+	move.w	%d1,%d2
+	jra	fp_e2e_extra1
+	| These are extremely small numbers, that will mostly end up as zero
+	| anyway, so this is only important for correct rounding.
+fp_e2e_small3:
+	bfffo	%d0{#24,#8},%d1
+	add.w	#40,%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+1:	move.w	%d2,(%a0)+
+	ext.l	%d1
+	jeq	fp_e2e_checkround
+	cmp.w	#8,%d1
+	jcs	2f
+1:	clr.b	(-4,%a0)
+	sub.w	#64,%d1
+	jcs	1f
+	add.w	#24,%d1
+	lsl.l	%d1,%d0
+	move.l	%d0,(%a0)
+	jra	fp_e2e_checkround
+1:	neg.w	%d1
+	bfins	%d0,(%a0){%d1,#8}
+	jra	fp_e2e_checkround
+2:	lsl.l	%d1,%d0
+	move.b	%d0,(-4,%a0)
+	lsr.l	#8,%d0
+	move.b	%d0,(7,%a0)
+	jra	fp_e2e_checkround
+#endif
+1:	move.l	%d0,%d1			| lower lword is splitted between
+	lsl.l	%d2,%d0			| higher and lower lword
+	move.l	%d0,(%a0)
+	move.l	%d1,%d0
+	neg.w	%d2
+	add.w	#32,%d2
+	lsr.l	%d2,%d0
+	move.l	%d0,-(%a0)
+	jra	fp_e2e_checkround
+	| Infinities and NaNs
+fp_e2e_large:
+	move.l	(%a0)+,%d0
+	jne	3f
+1:	tst.l	(%a0)
+	jne	4f
+	moveq	#1,%d0
+2:	subq.l	#8,%a0
+	printf	PCONV,"%p(",1,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,")\n"
+	rts
+	| we have maybe a NaN, shift off the highest bit
+3:	lsl.l	#1,%d0
+	jeq	1b
+	| we have a NaN, clear the return value
+4:	clrl	%d0
+	jra	2b
+
+
+/*
+ * Normalization functions.  Call these on the output of general
+ * FP operators, and before any conversion into the destination
+ * formats. fp_normalize_ext has always to be called first, the
+ * following conversion functions expect an already normalized
+ * number.
+ */
+
+	| fp_normalize_ext:
+	| normalize an extended in extended (unpacked) format, basically
+	| it does the same as fp_conv_ext2ext, additionally it also does
+	| the necessary postprocessing checks.
+	| args:	%a0 (struct fp_ext *)
+	| NOTE: it does _not_ modify %a0/%a1 and the upper word of %d2
+
+fp_normalize_ext:
+	printf	PNORM,"ne: %p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,"), "
+	move.l	(%a0)+,%d0
+	cmp.w	#0x7fff,%d0		| Inf / NaN?
+	jeq	fp_ne_large
+	move.l	(%a0),%d0
+	jpl	fp_ne_small		| zero / denorm?
+	| The high bit is set, so normalization is irrelevant.
+fp_ne_checkround:
+	subq.l	#4,%a0
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	move.b	(%a0),%d0
+	jne	fp_ne_round
+#endif
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+fp_ne_round:
+	fp_set_sr FPSR_EXC_INEX2
+	clr.b	(%a0)
+	move.w	(FPD_RND,FPDATA),%d2
+	jne	fp_ne_roundother	| %d2 == 0, round to nearest
+	tst.b	%d0			| test guard bit
+	jpl	9f			| zero is closer
+	btst	#0,(11,%a0)		| test lsb bit
+	jne	fp_ne_doroundup		| round to infinity
+	lsl.b	#1,%d0			| check low bits
+	jeq	9f			| round to zero
+fp_ne_doroundup:
+	addq.l	#1,(8,%a0)
+	jcc	9f
+	addq.l	#1,(4,%a0)
+	jcc	9f
+	addq.w	#1,(2,%a0)
+	move.w	#0x8000,(4,%a0)
+9:	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+fp_ne_roundother:
+	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	1f			| %d2 > 2, round to +infinity
+	tst.b	(1,%a0)			| to -inf
+	jne	fp_ne_doroundup		| negative, round to infinity
+	jra	9b			| positive, round to zero
+1:	tst.b	(1,%a0)			| to +inf
+	jeq	fp_ne_doroundup		| positive, round to infinity
+	jra	9b			| negative, round to zero
+#endif
+	| Zeros and subnormal numbers
+	| These are probably merely subnormal, rather than "denormalized"
+	|  numbers, so we will try to make them normal again.
+fp_ne_small:
+	jne	fp_ne_small1		| high lword zero?
+	move.l	(4,%a0),%d0
+	jne	fp_ne_small2
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	jne	fp_ne_small3
+#endif
+	| Genuine zero.
+	clr.w	-(%a0)
+	subq.l	#2,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+	| Subnormal.
+fp_ne_small1:
+	bfffo	%d0{#0,#32},%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+	fp_set_sr FPSR_EXC_UNFL
+1:	move.w	%d2,(%a0)+
+	move.w	%d1,%d2
+	jeq	fp_ne_checkround
+	| This is exactly the same 64-bit double shift as seen above.
+	lsl.l	%d2,%d0
+	move.l	%d0,(%a0)+
+	move.l	(%a0),%d0
+	move.l	%d0,%d1
+	lsl.l	%d2,%d0
+	move.l	%d0,(%a0)
+	neg.w	%d2
+	and.w	#0x1f,%d2
+	lsr.l	%d2,%d1
+	or.l	%d1,-(%a0)
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+fp_ne_extra1:
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	neg.w	%d2
+	add.w	#24,%d2
+	jcc	1f
+	clr.b	(-4,%a0)
+	lsl.l	%d2,%d0
+	or.l	%d0,(4,%a0)
+	jra	fp_ne_checkround
+1:	addq.w	#8,%d2
+	lsl.l	%d2,%d0
+	move.b	%d0,(-4,%a0)
+	lsr.l	#8,%d0
+	or.l	%d0,(4,%a0)
+#endif
+	jra	fp_ne_checkround
+	| May or may not be subnormal, if so, only 32 bits to shift.
+fp_ne_small2:
+	bfffo	%d0{#0,#32},%d1
+	add.w	#32,%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Beyond pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+	fp_set_sr FPSR_EXC_UNFL
+1:	move.w	%d2,(%a0)+
+	ext.l	%d1
+	jeq	fp_ne_checkround
+	clr.l	(4,%a0)
+	sub.w	#32,%d1
+	jcs	1f
+	lsl.l	%d1,%d0			| lower lword needs only to be shifted
+	move.l	%d0,(%a0)		| into the higher lword
+#ifdef CONFIG_FPU_EMU_EXTRAPREC
+	clr.l	%d0
+	move.b	(-4,%a0),%d0
+	clr.b	(-4,%a0)
+	neg.w	%d1
+	add.w	#32,%d1
+	bfins	%d0,(%a0){%d1,#8}
+#endif
+	jra	fp_ne_checkround
+1:	neg.w	%d1			| lower lword is splitted between
+	bfins	%d0,(%a0){%d1,#32}	| higher and lower lword
+#ifndef CONFIG_FPU_EMU_EXTRAPREC
+	jra	fp_ne_checkround
+#else
+	move.w	%d1,%d2
+	jra	fp_ne_extra1
+	| These are extremely small numbers, that will mostly end up as zero
+	| anyway, so this is only important for correct rounding.
+fp_ne_small3:
+	bfffo	%d0{#24,#8},%d1
+	add.w	#40,%d1
+	move.w	-(%a0),%d2
+	sub.w	%d1,%d2
+	jcc	1f
+	| Pathologically small, denormalize.
+	add.w	%d2,%d1
+	clr.w	%d2
+1:	move.w	%d2,(%a0)+
+	ext.l	%d1
+	jeq	fp_ne_checkround
+	cmp.w	#8,%d1
+	jcs	2f
+1:	clr.b	(-4,%a0)
+	sub.w	#64,%d1
+	jcs	1f
+	add.w	#24,%d1
+	lsl.l	%d1,%d0
+	move.l	%d0,(%a0)
+	jra	fp_ne_checkround
+1:	neg.w	%d1
+	bfins	%d0,(%a0){%d1,#8}
+	jra	fp_ne_checkround
+2:	lsl.l	%d1,%d0
+	move.b	%d0,(-4,%a0)
+	lsr.l	#8,%d0
+	move.b	%d0,(7,%a0)
+	jra	fp_ne_checkround
+#endif
+	| Infinities and NaNs, again, same as above.
+fp_ne_large:
+	move.l	(%a0)+,%d0
+	jne	3f
+1:	tst.l	(%a0)
+	jne	4f
+2:	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+	| we have maybe a NaN, shift off the highest bit
+3:	move.l	%d0,%d1
+	lsl.l	#1,%d1
+	jne	4f
+	clr.l	(-4,%a0)
+	jra	1b
+	| we have a NaN, test if it is signaling
+4:	bset	#30,%d0
+	jne	2b
+	fp_set_sr FPSR_EXC_SNAN
+	move.l	%d0,(-4,%a0)
+	jra	2b
+
+	| these next two do rounding as per the IEEE standard.
+	| values for the rounding modes appear to be:
+	| 0:	Round to nearest
+	| 1:	Round to zero
+	| 2:	Round to -Infinity
+	| 3:	Round to +Infinity
+	| both functions expect that fp_normalize was already
+	| called (and extended argument is already normalized
+	| as far as possible), these are used if there is different
+	| rounding precision is selected and before converting
+	| into single/double
+
+	| fp_normalize_double:
+	| normalize an extended with double (52-bit) precision
+	| args:	 %a0 (struct fp_ext *)
+
+fp_normalize_double:
+	printf	PNORM,"nd: %p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,"), "
+	move.l	(%a0)+,%d2
+	tst.w	%d2
+	jeq	fp_nd_zero		| zero / denormalized
+	cmp.w	#0x7fff,%d2
+	jeq	fp_nd_huge		| NaN / infinitive.
+	sub.w	#0x4000-0x3ff,%d2	| will the exponent fit?
+	jcs	fp_nd_small		| too small.
+	cmp.w	#0x7fe,%d2
+	jcc	fp_nd_large		| too big.
+	addq.l	#4,%a0
+	move.l	(%a0),%d0		| low lword of mantissa
+	| now, round off the low 11 bits.
+fp_nd_round:
+	moveq	#21,%d1
+	lsl.l	%d1,%d0			| keep 11 low bits.
+	jne	fp_nd_checkround	| Are they non-zero?
+	| nothing to do here
+9:	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+	| Be careful with the X bit! It contains the lsb
+	| from the shift above, it is needed for round to nearest.
+fp_nd_checkround:
+	fp_set_sr FPSR_EXC_INEX2	| INEX2 bit
+	and.w	#0xf800,(2,%a0)		| clear bits 0-10
+	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	jne	2f			| %d2 == 0, round to nearest
+	tst.l	%d0			| test guard bit
+	jpl	9b			| zero is closer
+	| here we test the X bit by adding it to %d2
+	clr.w	%d2			| first set z bit, addx only clears it
+	addx.w	%d2,%d2			| test lsb bit
+	| IEEE754-specified "round to even" behaviour.  If the guard
+	| bit is set, then the number is odd, so rounding works like
+	| in grade-school arithmetic (i.e. 1.5 rounds to 2.0)
+	| Otherwise, an equal distance rounds towards zero, so as not
+	| to produce an odd number.  This is strange, but it is what
+	| the standard says.
+	jne	fp_nd_doroundup		| round to infinity
+	lsl.l	#1,%d0			| check low bits
+	jeq	9b			| round to zero
+fp_nd_doroundup:
+	| round (the mantissa, that is) towards infinity
+	add.l	#0x800,(%a0)
+	jcc	9b			| no overflow, good.
+	addq.l	#1,-(%a0)		| extend to high lword
+	jcc	1f			| no overflow, good.
+	| Yow! we have managed to overflow the mantissa.  Since this
+	| only happens when %d1 was 0xfffff800, it is now zero, so
+	| reset the high bit, and increment the exponent.
+	move.w	#0x8000,(%a0)
+	addq.w	#1,-(%a0)
+	cmp.w	#0x43ff,(%a0)+		| exponent now overflown?
+	jeq	fp_nd_large		| yes, so make it infinity.
+1:	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+2:	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	3f			| %d2 > 2, round to +infinity
+	| Round to +Inf or -Inf.  High word of %d2 contains the
+	| sign of the number, by the way.
+	swap	%d2			| to -inf
+	tst.b	%d2
+	jne	fp_nd_doroundup		| negative, round to infinity
+	jra	9b			| positive, round to zero
+3:	swap	%d2			| to +inf
+	tst.b	%d2
+	jeq	fp_nd_doroundup		| positive, round to infinity
+	jra	9b			| negative, round to zero
+	| Exponent underflow.  Try to make a denormal, and set it to
+	| the smallest possible fraction if this fails.
+fp_nd_small:
+	fp_set_sr FPSR_EXC_UNFL		| set UNFL bit
+	move.w	#0x3c01,(-2,%a0)	| 2**-1022
+	neg.w	%d2			| degree of underflow
+	cmp.w	#32,%d2			| single or double shift?
+	jcc	1f
+	| Again, another 64-bit double shift.
+	move.l	(%a0),%d0
+	move.l	%d0,%d1
+	lsr.l	%d2,%d0
+	move.l	%d0,(%a0)+
+	move.l	(%a0),%d0
+	lsr.l	%d2,%d0
+	neg.w	%d2
+	add.w	#32,%d2
+	lsl.l	%d2,%d1
+	or.l	%d1,%d0
+	move.l	(%a0),%d1
+	move.l	%d0,(%a0)
+	| Check to see if we shifted off any significant bits
+	lsl.l	%d2,%d1
+	jeq	fp_nd_round		| Nope, round.
+	bset	#0,%d0			| Yes, so set the "sticky bit".
+	jra	fp_nd_round		| Now, round.
+	| Another 64-bit single shift and store
+1:	sub.w	#32,%d2
+	cmp.w	#32,%d2			| Do we really need to shift?
+	jcc	2f			| No, the number is too small.
+	move.l	(%a0),%d0
+	clr.l	(%a0)+
+	move.l	%d0,%d1
+	lsr.l	%d2,%d0
+	neg.w	%d2
+	add.w	#32,%d2
+	| Again, check to see if we shifted off any significant bits.
+	tst.l	(%a0)
+	jeq	1f
+	bset	#0,%d0			| Sticky bit.
+1:	move.l	%d0,(%a0)
+	lsl.l	%d2,%d1
+	jeq	fp_nd_round
+	bset	#0,%d0
+	jra	fp_nd_round
+	| Sorry, the number is just too small.
+2:	clr.l	(%a0)+
+	clr.l	(%a0)
+	moveq	#1,%d0			| Smallest possible fraction,
+	jra	fp_nd_round		| round as desired.
+	| zero and denormalized
+fp_nd_zero:
+	tst.l	(%a0)+
+	jne	1f
+	tst.l	(%a0)
+	jne	1f
+	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts				| zero.  nothing to do.
+	| These are not merely subnormal numbers, but true denormals,
+	| i.e. pathologically small (exponent is 2**-16383) numbers.
+	| It is clearly impossible for even a normal extended number
+	| with that exponent to fit into double precision, so just
+	| write these ones off as "too darn small".
+1:	fp_set_sr FPSR_EXC_UNFL		| Set UNFL bit
+	clr.l	(%a0)
+	clr.l	-(%a0)
+	move.w	#0x3c01,-(%a0)		| i.e. 2**-1022
+	addq.l	#6,%a0
+	moveq	#1,%d0
+	jra	fp_nd_round		| round.
+	| Exponent overflow.  Just call it infinity.
+fp_nd_large:
+	move.w	#0x7ff,%d0
+	and.w	(6,%a0),%d0
+	jeq	1f
+	fp_set_sr FPSR_EXC_INEX2
+1:	fp_set_sr FPSR_EXC_OVFL
+	move.w	(FPD_RND,FPDATA),%d2
+	jne	3f			| %d2 = 0 round to nearest
+1:	move.w	#0x7fff,(-2,%a0)
+	clr.l	(%a0)+
+	clr.l	(%a0)
+2:	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+3:	subq.w	#2,%d2
+	jcs	5f			| %d2 < 2, round to zero
+	jhi	4f			| %d2 > 2, round to +infinity
+	tst.b	(-3,%a0)		| to -inf
+	jne	1b
+	jra	5f
+4:	tst.b	(-3,%a0)		| to +inf
+	jeq	1b
+5:	move.w	#0x43fe,(-2,%a0)
+	moveq	#-1,%d0
+	move.l	%d0,(%a0)+
+	move.w	#0xf800,%d0
+	move.l	%d0,(%a0)
+	jra	2b
+	| Infinities or NaNs
+fp_nd_huge:
+	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+
+	| fp_normalize_single:
+	| normalize an extended with single (23-bit) precision
+	| args:	 %a0 (struct fp_ext *)
+
+fp_normalize_single:
+	printf	PNORM,"ns: %p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,") "
+	addq.l	#2,%a0
+	move.w	(%a0)+,%d2
+	jeq	fp_ns_zero		| zero / denormalized
+	cmp.w	#0x7fff,%d2
+	jeq	fp_ns_huge		| NaN / infinitive.
+	sub.w	#0x4000-0x7f,%d2	| will the exponent fit?
+	jcs	fp_ns_small		| too small.
+	cmp.w	#0xfe,%d2
+	jcc	fp_ns_large		| too big.
+	move.l	(%a0)+,%d0		| get high lword of mantissa
+fp_ns_round:
+	tst.l	(%a0)			| check the low lword
+	jeq	1f
+	| Set a sticky bit if it is non-zero.  This should only
+	| affect the rounding in what would otherwise be equal-
+	| distance situations, which is what we want it to do.
+	bset	#0,%d0
+1:	clr.l	(%a0)			| zap it from memory.
+	| now, round off the low 8 bits of the hi lword.
+	tst.b	%d0			| 8 low bits.
+	jne	fp_ns_checkround	| Are they non-zero?
+	| nothing to do here
+	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+fp_ns_checkround:
+	fp_set_sr FPSR_EXC_INEX2	| INEX2 bit
+	clr.b	-(%a0)			| clear low byte of high lword
+	subq.l	#3,%a0
+	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	jne	2f			| %d2 == 0, round to nearest
+	tst.b	%d0			| test guard bit
+	jpl	9f			| zero is closer
+	btst	#8,%d0			| test lsb bit
+	| round to even behaviour, see above.
+	jne	fp_ns_doroundup		| round to infinity
+	lsl.b	#1,%d0			| check low bits
+	jeq	9f			| round to zero
+fp_ns_doroundup:
+	| round (the mantissa, that is) towards infinity
+	add.l	#0x100,(%a0)
+	jcc	9f			| no overflow, good.
+	| Overflow.  This means that the %d1 was 0xffffff00, so it
+	| is now zero.  We will set the mantissa to reflect this, and
+	| increment the exponent (checking for overflow there too)
+	move.w	#0x8000,(%a0)
+	addq.w	#1,-(%a0)
+	cmp.w	#0x407f,(%a0)+		| exponent now overflown?
+	jeq	fp_ns_large		| yes, so make it infinity.
+9:	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+	| check nondefault rounding modes
+2:	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	3f			| %d2 > 2, round to +infinity
+	tst.b	(-3,%a0)		| to -inf
+	jne	fp_ns_doroundup		| negative, round to infinity
+	jra	9b			| positive, round to zero
+3:	tst.b	(-3,%a0)		| to +inf
+	jeq	fp_ns_doroundup		| positive, round to infinity
+	jra	9b			| negative, round to zero
+	| Exponent underflow.  Try to make a denormal, and set it to
+	| the smallest possible fraction if this fails.
+fp_ns_small:
+	fp_set_sr FPSR_EXC_UNFL		| set UNFL bit
+	move.w	#0x3f81,(-2,%a0)	| 2**-126
+	neg.w	%d2			| degree of underflow
+	cmp.w	#32,%d2			| single or double shift?
+	jcc	2f
+	| a 32-bit shift.
+	move.l	(%a0),%d0
+	move.l	%d0,%d1
+	lsr.l	%d2,%d0
+	move.l	%d0,(%a0)+
+	| Check to see if we shifted off any significant bits.
+	neg.w	%d2
+	add.w	#32,%d2
+	lsl.l	%d2,%d1
+	jeq	1f
+	bset	#0,%d0			| Sticky bit.
+	| Check the lower lword
+1:	tst.l	(%a0)
+	jeq	fp_ns_round
+	clr	(%a0)
+	bset	#0,%d0			| Sticky bit.
+	jra	fp_ns_round
+	| Sorry, the number is just too small.
+2:	clr.l	(%a0)+
+	clr.l	(%a0)
+	moveq	#1,%d0			| Smallest possible fraction,
+	jra	fp_ns_round		| round as desired.
+	| Exponent overflow.  Just call it infinity.
+fp_ns_large:
+	tst.b	(3,%a0)
+	jeq	1f
+	fp_set_sr FPSR_EXC_INEX2
+1:	fp_set_sr FPSR_EXC_OVFL
+	move.w	(FPD_RND,FPDATA),%d2
+	jne	3f			| %d2 = 0 round to nearest
+1:	move.w	#0x7fff,(-2,%a0)
+	clr.l	(%a0)+
+	clr.l	(%a0)
+2:	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+3:	subq.w	#2,%d2
+	jcs	5f			| %d2 < 2, round to zero
+	jhi	4f			| %d2 > 2, round to +infinity
+	tst.b	(-3,%a0)		| to -inf
+	jne	1b
+	jra	5f
+4:	tst.b	(-3,%a0)		| to +inf
+	jeq	1b
+5:	move.w	#0x407e,(-2,%a0)
+	move.l	#0xffffff00,(%a0)+
+	clr.l	(%a0)
+	jra	2b
+	| zero and denormalized
+fp_ns_zero:
+	tst.l	(%a0)+
+	jne	1f
+	tst.l	(%a0)
+	jne	1f
+	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts				| zero.  nothing to do.
+	| These are not merely subnormal numbers, but true denormals,
+	| i.e. pathologically small (exponent is 2**-16383) numbers.
+	| It is clearly impossible for even a normal extended number
+	| with that exponent to fit into single precision, so just
+	| write these ones off as "too darn small".
+1:	fp_set_sr FPSR_EXC_UNFL		| Set UNFL bit
+	clr.l	(%a0)
+	clr.l	-(%a0)
+	move.w	#0x3f81,-(%a0)		| i.e. 2**-126
+	addq.l	#6,%a0
+	moveq	#1,%d0
+	jra	fp_ns_round		| round.
+	| Infinities or NaNs
+fp_ns_huge:
+	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+
+	| fp_normalize_single_fast:
+	| normalize an extended with single (23-bit) precision
+	| this is only used by fsgldiv/fsgdlmul, where the
+	| operand is not completly normalized.
+	| args:	 %a0 (struct fp_ext *)
+
+fp_normalize_single_fast:
+	printf	PNORM,"nsf: %p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,") "
+	addq.l	#2,%a0
+	move.w	(%a0)+,%d2
+	cmp.w	#0x7fff,%d2
+	jeq	fp_nsf_huge		| NaN / infinitive.
+	move.l	(%a0)+,%d0		| get high lword of mantissa
+fp_nsf_round:
+	tst.l	(%a0)			| check the low lword
+	jeq	1f
+	| Set a sticky bit if it is non-zero.  This should only
+	| affect the rounding in what would otherwise be equal-
+	| distance situations, which is what we want it to do.
+	bset	#0,%d0
+1:	clr.l	(%a0)			| zap it from memory.
+	| now, round off the low 8 bits of the hi lword.
+	tst.b	%d0			| 8 low bits.
+	jne	fp_nsf_checkround	| Are they non-zero?
+	| nothing to do here
+	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+fp_nsf_checkround:
+	fp_set_sr FPSR_EXC_INEX2	| INEX2 bit
+	clr.b	-(%a0)			| clear low byte of high lword
+	subq.l	#3,%a0
+	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	jne	2f			| %d2 == 0, round to nearest
+	tst.b	%d0			| test guard bit
+	jpl	9f			| zero is closer
+	btst	#8,%d0			| test lsb bit
+	| round to even behaviour, see above.
+	jne	fp_nsf_doroundup		| round to infinity
+	lsl.b	#1,%d0			| check low bits
+	jeq	9f			| round to zero
+fp_nsf_doroundup:
+	| round (the mantissa, that is) towards infinity
+	add.l	#0x100,(%a0)
+	jcc	9f			| no overflow, good.
+	| Overflow.  This means that the %d1 was 0xffffff00, so it
+	| is now zero.  We will set the mantissa to reflect this, and
+	| increment the exponent (checking for overflow there too)
+	move.w	#0x8000,(%a0)
+	addq.w	#1,-(%a0)
+	cmp.w	#0x407f,(%a0)+		| exponent now overflown?
+	jeq	fp_nsf_large		| yes, so make it infinity.
+9:	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+	| check nondefault rounding modes
+2:	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	3f			| %d2 > 2, round to +infinity
+	tst.b	(-3,%a0)		| to -inf
+	jne	fp_nsf_doroundup	| negative, round to infinity
+	jra	9b			| positive, round to zero
+3:	tst.b	(-3,%a0)		| to +inf
+	jeq	fp_nsf_doroundup		| positive, round to infinity
+	jra	9b			| negative, round to zero
+	| Exponent overflow.  Just call it infinity.
+fp_nsf_large:
+	tst.b	(3,%a0)
+	jeq	1f
+	fp_set_sr FPSR_EXC_INEX2
+1:	fp_set_sr FPSR_EXC_OVFL
+	move.w	(FPD_RND,FPDATA),%d2
+	jne	3f			| %d2 = 0 round to nearest
+1:	move.w	#0x7fff,(-2,%a0)
+	clr.l	(%a0)+
+	clr.l	(%a0)
+2:	subq.l	#8,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+3:	subq.w	#2,%d2
+	jcs	5f			| %d2 < 2, round to zero
+	jhi	4f			| %d2 > 2, round to +infinity
+	tst.b	(-3,%a0)		| to -inf
+	jne	1b
+	jra	5f
+4:	tst.b	(-3,%a0)		| to +inf
+	jeq	1b
+5:	move.w	#0x407e,(-2,%a0)
+	move.l	#0xffffff00,(%a0)+
+	clr.l	(%a0)
+	jra	2b
+	| Infinities or NaNs
+fp_nsf_huge:
+	subq.l	#4,%a0
+	printf	PNORM,"%p(",1,%a0
+	printx	PNORM,%a0@
+	printf	PNORM,")\n"
+	rts
+
+	| conv_ext2int (macro):
+	| Generates a subroutine that converts an extended value to an
+	| integer of a given size, again, with the appropriate type of
+	| rounding.
+
+	| Macro arguments:
+	| s:	size, as given in an assembly instruction.
+	| b:	number of bits in that size.
+
+	| Subroutine arguments:
+	| %a0:	source (struct fp_ext *)
+
+	| Returns the integer in %d0 (like it should)
+
+.macro conv_ext2int s,b
+	.set	inf,(1<<(\b-1))-1	| i.e. MAXINT
+	printf	PCONV,"e2i%d: %p(",2,#\b,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,") "
+	addq.l	#2,%a0
+	move.w	(%a0)+,%d2		| exponent
+	jeq	fp_e2i_zero\b		| zero / denorm (== 0, here)
+	cmp.w	#0x7fff,%d2
+	jeq	fp_e2i_huge\b		| Inf / NaN
+	sub.w	#0x3ffe,%d2
+	jcs	fp_e2i_small\b
+	cmp.w	#\b,%d2
+	jhi	fp_e2i_large\b
+	move.l	(%a0),%d0
+	move.l	%d0,%d1
+	lsl.l	%d2,%d1
+	jne	fp_e2i_round\b
+	tst.l	(4,%a0)
+	jne	fp_e2i_round\b
+	neg.w	%d2
+	add.w	#32,%d2
+	lsr.l	%d2,%d0
+9:	tst.w	(-4,%a0)
+	jne	1f
+	tst.\s	%d0
+	jmi	fp_e2i_large\b
+	printf	PCONV,"-> %p\n",1,%d0
+	rts
+1:	neg.\s	%d0
+	jeq	1f
+	jpl	fp_e2i_large\b
+1:	printf	PCONV,"-> %p\n",1,%d0
+	rts
+fp_e2i_round\b:
+	fp_set_sr FPSR_EXC_INEX2	| INEX2 bit
+	neg.w	%d2
+	add.w	#32,%d2
+	.if	\b>16
+	jeq	5f
+	.endif
+	lsr.l	%d2,%d0
+	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	jne	2f			| %d2 == 0, round to nearest
+	tst.l	%d1			| test guard bit
+	jpl	9b			| zero is closer
+	btst	%d2,%d0			| test lsb bit (%d2 still 0)
+	jne	fp_e2i_doroundup\b
+	lsl.l	#1,%d1			| check low bits
+	jne	fp_e2i_doroundup\b
+	tst.l	(4,%a0)
+	jeq	9b
+fp_e2i_doroundup\b:
+	addq.l	#1,%d0
+	jra	9b
+	| check nondefault rounding modes
+2:	subq.w	#2,%d2
+	jcs	9b			| %d2 < 2, round to zero
+	jhi	3f			| %d2 > 2, round to +infinity
+	tst.w	(-4,%a0)		| to -inf
+	jne	fp_e2i_doroundup\b	| negative, round to infinity
+	jra	9b			| positive, round to zero
+3:	tst.w	(-4,%a0)		| to +inf
+	jeq	fp_e2i_doroundup\b	| positive, round to infinity
+	jra	9b	| negative, round to zero
+	| we are only want -2**127 get correctly rounded here,
+	| since the guard bit is in the lower lword.
+	| everything else ends up anyway as overflow.
+	.if	\b>16
+5:	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	jne	2b			| %d2 == 0, round to nearest
+	move.l	(4,%a0),%d1		| test guard bit
+	jpl	9b			| zero is closer
+	lsl.l	#1,%d1			| check low bits
+	jne	fp_e2i_doroundup\b
+	jra	9b
+	.endif
+fp_e2i_zero\b:
+	clr.l	%d0
+	tst.l	(%a0)+
+	jne	1f
+	tst.l	(%a0)
+	jeq	3f
+1:	subq.l	#4,%a0
+	fp_clr_sr FPSR_EXC_UNFL		| fp_normalize_ext has set this bit
+fp_e2i_small\b:
+	fp_set_sr FPSR_EXC_INEX2
+	clr.l	%d0
+	move.w	(FPD_RND,FPDATA),%d2	| rounding mode
+	subq.w	#2,%d2
+	jcs	3f			| %d2 < 2, round to nearest/zero
+	jhi	2f			| %d2 > 2, round to +infinity
+	tst.w	(-4,%a0)		| to -inf
+	jeq	3f
+	subq.\s	#1,%d0
+	jra	3f
+2:	tst.w	(-4,%a0)		| to +inf
+	jne	3f
+	addq.\s	#1,%d0
+3:	printf	PCONV,"-> %p\n",1,%d0
+	rts
+fp_e2i_large\b:
+	fp_set_sr FPSR_EXC_OPERR
+	move.\s	#inf,%d0
+	tst.w	(-4,%a0)
+	jeq	1f
+	addq.\s	#1,%d0
+1:	printf	PCONV,"-> %p\n",1,%d0
+	rts
+fp_e2i_huge\b:
+	move.\s	(%a0),%d0
+	tst.l	(%a0)
+	jne	1f
+	tst.l	(%a0)
+	jeq	fp_e2i_large\b
+	| fp_normalize_ext has set this bit already
+	| and made the number nonsignaling
+1:	fp_tst_sr FPSR_EXC_SNAN
+	jne	1f
+	fp_set_sr FPSR_EXC_OPERR
+1:	printf	PCONV,"-> %p\n",1,%d0
+	rts
+.endm
+
+fp_conv_ext2long:
+	conv_ext2int l,32
+
+fp_conv_ext2short:
+	conv_ext2int w,16
+
+fp_conv_ext2byte:
+	conv_ext2int b,8
+
+fp_conv_ext2double:
+	jsr	fp_normalize_double
+	printf	PCONV,"e2d: %p(",1,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,"), "
+	move.l	(%a0)+,%d2
+	cmp.w	#0x7fff,%d2
+	jne	1f
+	move.w	#0x7ff,%d2
+	move.l	(%a0)+,%d0
+	jra	2f
+1:	sub.w	#0x3fff-0x3ff,%d2
+	move.l	(%a0)+,%d0
+	jmi	2f
+	clr.w	%d2
+2:	lsl.w	#5,%d2
+	lsl.l	#7,%d2
+	lsl.l	#8,%d2
+	move.l	%d0,%d1
+	lsl.l	#1,%d0
+	lsr.l	#4,%d0
+	lsr.l	#8,%d0
+	or.l	%d2,%d0
+	putuser.l %d0,(%a1)+,fp_err_ua2,%a1
+	moveq	#21,%d0
+	lsl.l	%d0,%d1
+	move.l	(%a0),%d0
+	lsr.l	#4,%d0
+	lsr.l	#7,%d0
+	or.l	%d1,%d0
+	putuser.l %d0,(%a1),fp_err_ua2,%a1
+#ifdef FPU_EMU_DEBUG
+	getuser.l %a1@(-4),%d0,fp_err_ua2,%a1
+	getuser.l %a1@(0),%d1,fp_err_ua2,%a1
+	printf	PCONV,"%p(%08x%08x)\n",3,%a1,%d0,%d1
+#endif
+	rts
+
+fp_conv_ext2single:
+	jsr	fp_normalize_single
+	printf	PCONV,"e2s: %p(",1,%a0
+	printx	PCONV,%a0@
+	printf	PCONV,"), "
+	move.l	(%a0)+,%d1
+	cmp.w	#0x7fff,%d1
+	jne	1f
+	move.w	#0xff,%d1
+	move.l	(%a0)+,%d0
+	jra	2f
+1:	sub.w	#0x3fff-0x7f,%d1
+	move.l	(%a0)+,%d0
+	jmi	2f
+	clr.w	%d1
+2:	lsl.w	#8,%d1
+	lsl.l	#7,%d1
+	lsl.l	#8,%d1
+	bclr	#31,%d0
+	lsr.l	#8,%d0
+	or.l	%d1,%d0
+	printf	PCONV,"%08x\n",1,%d0
+	rts
+
+	| special return addresses for instr that
+	| encode the rounding precision in the opcode
+	| (e.g. fsmove,fdmove)
+
+fp_finalrounding_single:
+	addq.l	#8,%sp
+	jsr	fp_normalize_ext
+	jsr	fp_normalize_single
+	jra	fp_finaltest
+
+fp_finalrounding_single_fast:
+	addq.l	#8,%sp 
+	jsr	fp_normalize_ext
+	jsr	fp_normalize_single_fast
+	jra	fp_finaltest
+
+fp_finalrounding_double:
+	addq.l	#8,%sp
+	jsr	fp_normalize_ext
+	jsr	fp_normalize_double
+	jra	fp_finaltest
+
+	| fp_finaltest:
+	| set the emulated status register based on the outcome of an
+	| emulated instruction.
+
+fp_finalrounding:
+	addq.l	#8,%sp
+|	printf	,"f: %p\n",1,%a0
+	jsr	fp_normalize_ext
+	move.w	(FPD_PREC,FPDATA),%d0
+	subq.w	#1,%d0
+	jcs	fp_finaltest
+	jne	1f
+	jsr	fp_normalize_single
+	jra	2f
+1:	jsr	fp_normalize_double
+2:|	printf	,"f: %p\n",1,%a0
+fp_finaltest:
+	| First, we do some of the obvious tests for the exception
+	| status byte and condition code bytes of fp_sr here, so that
+	| they do not have to be handled individually by every
+	| emulated instruction.
+	clr.l	%d0
+	addq.l	#1,%a0
+	tst.b	(%a0)+			| sign
+	jeq	1f
+	bset	#FPSR_CC_NEG-24,%d0	| N bit
+1:	cmp.w	#0x7fff,(%a0)+		| exponent
+	jeq	2f
+	| test for zero
+	moveq	#FPSR_CC_Z-24,%d1
+	tst.l	(%a0)+
+	jne	9f
+	tst.l	(%a0)
+	jne	9f
+	jra	8f
+	| infinitiv and NAN
+2:	moveq	#FPSR_CC_NAN-24,%d1
+	move.l	(%a0)+,%d2
+	lsl.l	#1,%d2			| ignore high bit
+	jne	8f
+	tst.l	(%a0)
+	jne	8f
+	moveq	#FPSR_CC_INF-24,%d1
+8:	bset	%d1,%d0
+9:	move.b	%d0,(FPD_FPSR+0,FPDATA)	| set condition test result
+	| move instructions enter here
+	| Here, we test things in the exception status byte, and set
+	| other things in the accrued exception byte accordingly.
+	| Emulated instructions can set various things in the former,
+	| as defined in fp_emu.h.
+fp_final:
+	move.l	(FPD_FPSR,FPDATA),%d0
+#if 0
+	btst	#FPSR_EXC_SNAN,%d0	| EXC_SNAN
+	jne	1f
+	btst	#FPSR_EXC_OPERR,%d0	| EXC_OPERR
+	jeq	2f
+1:	bset	#FPSR_AEXC_IOP,%d0	| set IOP bit
+2:	btst	#FPSR_EXC_OVFL,%d0	| EXC_OVFL
+	jeq	1f
+	bset	#FPSR_AEXC_OVFL,%d0	| set OVFL bit
+1:	btst	#FPSR_EXC_UNFL,%d0	| EXC_UNFL
+	jeq	1f
+	btst	#FPSR_EXC_INEX2,%d0	| EXC_INEX2
+	jeq	1f
+	bset	#FPSR_AEXC_UNFL,%d0	| set UNFL bit
+1:	btst	#FPSR_EXC_DZ,%d0	| EXC_INEX1
+	jeq	1f
+	bset	#FPSR_AEXC_DZ,%d0	| set DZ bit
+1:	btst	#FPSR_EXC_OVFL,%d0	| EXC_OVFL
+	jne	1f
+	btst	#FPSR_EXC_INEX2,%d0	| EXC_INEX2
+	jne	1f
+	btst	#FPSR_EXC_INEX1,%d0	| EXC_INEX1
+	jeq	2f
+1:	bset	#FPSR_AEXC_INEX,%d0	| set INEX bit
+2:	move.l	%d0,(FPD_FPSR,FPDATA)
+#else
+	| same as above, greatly optimized, but untested (yet)
+	move.l	%d0,%d2
+	lsr.l	#5,%d0
+	move.l	%d0,%d1
+	lsr.l	#4,%d1
+	or.l	%d0,%d1
+	and.b	#0x08,%d1
+	move.l	%d2,%d0
+	lsr.l	#6,%d0
+	or.l	%d1,%d0
+	move.l	%d2,%d1
+	lsr.l	#4,%d1
+	or.b	#0xdf,%d1
+	and.b	%d1,%d0
+	move.l	%d2,%d1
+	lsr.l	#7,%d1
+	and.b	#0x80,%d1
+	or.b	%d1,%d0
+	and.b	#0xf8,%d0
+	or.b	%d0,%d2
+	move.l	%d2,(FPD_FPSR,FPDATA)
+#endif
+	move.b	(FPD_FPSR+2,FPDATA),%d0
+	and.b	(FPD_FPCR+2,FPDATA),%d0
+	jeq	1f
+	printf	,"send signal!!!\n"
+1:	jra	fp_end
diff -urN linux-2.2.18-pre15/arch/m68k/math-emu/multi_arith.h linux/arch/m68k/math-emu/multi_arith.h
--- linux-2.2.18-pre15/arch/m68k/math-emu/multi_arith.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/math-emu/multi_arith.h	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,822 @@
+/* multi_arith.h: multi-precision integer arithmetic functions, needed
+   to do extended-precision floating point.
+
+   (c) 1998 David Huggins-Daines.
+
+   Somewhat based on arch/alpha/math-emu/ieee-math.c, which is (c)
+   David Mosberger-Tang.
+
+   You may copy, modify, and redistribute this file under the terms of
+   the GNU General Public License, version 2, or any later version, at
+   your convenience. */
+
+/* Note:
+
+   These are not general multi-precision math routines.  Rather, they
+   implement the subset of integer arithmetic that we need in order to
+   multiply, divide, and normalize 128-bit unsigned mantissae.  */
+
+#ifndef MULTI_ARITH_H
+#define MULTI_ARITH_H
+
+#if 0	/* old code... */
+
+/* Unsigned only, because we don't need signs to multiply and divide. */
+typedef unsigned int int128[4];
+
+/* Word order */
+enum {
+	MSW128,
+	NMSW128,
+	NLSW128,
+	LSW128
+};
+
+/* big-endian */
+#define LO_WORD(ll) (((unsigned int *) &ll)[1])
+#define HI_WORD(ll) (((unsigned int *) &ll)[0])
+
+/* Convenience functions to stuff various integer values into int128s */
+
+extern inline void zero128(int128 a)
+{
+	a[LSW128] = a[NLSW128] = a[NMSW128] = a[MSW128] = 0;
+}
+
+/* Human-readable word order in the arguments */
+extern inline void set128(unsigned int i3,
+			  unsigned int i2,
+			  unsigned int i1,
+			  unsigned int i0,
+			  int128 a)
+{
+	a[LSW128] = i0;
+	a[NLSW128] = i1;
+	a[NMSW128] = i2;
+	a[MSW128] = i3;
+}
+
+/* Convenience functions (for testing as well) */
+extern inline void int64_to_128(unsigned long long src,
+				int128 dest)
+{
+	dest[LSW128] = (unsigned int) src;
+	dest[NLSW128] = src >> 32;
+	dest[NMSW128] = dest[MSW128] = 0;
+}
+
+extern inline void int128_to_64(const int128 src,
+				unsigned long long *dest)
+{
+	*dest = src[LSW128] | (long long) src[NLSW128] << 32;
+}
+
+extern inline void put_i128(const int128 a)
+{
+	printk("%08x %08x %08x %08x\n", a[MSW128], a[NMSW128],
+	       a[NLSW128], a[LSW128]);
+}
+
+/* Internal shifters:
+
+   Note that these are only good for 0 < count < 32.
+ */
+
+extern inline void _lsl128(unsigned int count, int128 a)
+{
+	a[MSW128] = (a[MSW128] << count) | (a[NMSW128] >> (32 - count));
+	a[NMSW128] = (a[NMSW128] << count) | (a[NLSW128] >> (32 - count));
+	a[NLSW128] = (a[NLSW128] << count) | (a[LSW128] >> (32 - count));
+	a[LSW128] <<= count;
+}
+
+extern inline void _lsr128(unsigned int count, int128 a)
+{
+	a[LSW128] = (a[LSW128] >> count) | (a[NLSW128] << (32 - count));
+	a[NLSW128] = (a[NLSW128] >> count) | (a[NMSW128] << (32 - count));
+	a[NMSW128] = (a[NMSW128] >> count) | (a[MSW128] << (32 - count));
+	a[MSW128] >>= count;
+}
+
+/* Should be faster, one would hope */
+
+extern inline void lslone128(int128 a)
+{
+	asm volatile ("lsl.l #1,%0\n"
+		      "roxl.l #1,%1\n"
+		      "roxl.l #1,%2\n"
+		      "roxl.l #1,%3\n"
+		      :
+		      "=d" (a[LSW128]),
+		      "=d"(a[NLSW128]),
+		      "=d"(a[NMSW128]),
+		      "=d"(a[MSW128])
+		      :
+		      "0"(a[LSW128]),
+		      "1"(a[NLSW128]),
+		      "2"(a[NMSW128]),
+		      "3"(a[MSW128]));
+}
+
+extern inline void lsrone128(int128 a)
+{
+	asm volatile ("lsr.l #1,%0\n"
+		      "roxr.l #1,%1\n"
+		      "roxr.l #1,%2\n"
+		      "roxr.l #1,%3\n"
+		      :
+		      "=d" (a[MSW128]),
+		      "=d"(a[NMSW128]),
+		      "=d"(a[NLSW128]),
+		      "=d"(a[LSW128])
+		      :
+		      "0"(a[MSW128]),
+		      "1"(a[NMSW128]),
+		      "2"(a[NLSW128]),
+		      "3"(a[LSW128]));
+}
+
+/* Generalized 128-bit shifters:
+
+   These bit-shift to a multiple of 32, then move whole longwords.  */
+
+extern inline void lsl128(unsigned int count, int128 a)
+{
+	int wordcount, i;
+
+	if (count % 32)
+		_lsl128(count % 32, a);
+
+	if (0 == (wordcount = count / 32))
+		return;
+
+	/* argh, gak, endian-sensitive */
+	for (i = 0; i < 4 - wordcount; i++) {
+		a[i] = a[i + wordcount];
+	}
+	for (i = 3; i >= 4 - wordcount; --i) {
+		a[i] = 0;
+	}
+}
+
+extern inline void lsr128(unsigned int count, int128 a)
+{
+	int wordcount, i;
+
+	if (count % 32)
+		_lsr128(count % 32, a);
+
+	if (0 == (wordcount = count / 32))
+		return;
+
+	for (i = 3; i >= wordcount; --i) {
+		a[i] = a[i - wordcount];
+	}
+	for (i = 0; i < wordcount; i++) {
+		a[i] = 0;
+	}
+}
+
+extern inline int orl128(int a, int128 b)
+{
+	b[LSW128] |= a;
+}
+
+extern inline int btsthi128(const int128 a)
+{
+	return a[MSW128] & 0x80000000;
+}
+
+/* test bits (numbered from 0 = LSB) up to and including "top" */
+extern inline int bftestlo128(int top, const int128 a)
+{
+	int r = 0;
+
+	if (top > 31)
+		r |= a[LSW128];
+	if (top > 63)
+		r |= a[NLSW128];
+	if (top > 95)
+		r |= a[NMSW128];
+
+	r |= a[3 - (top / 32)] & ((1 << (top % 32 + 1)) - 1);
+
+	return (r != 0);
+}
+
+/* Aargh.  We need these because GCC is broken */
+/* FIXME: do them in assembly, for goodness' sake! */
+extern inline void mask64(int pos, unsigned long long *mask)
+{
+	*mask = 0;
+
+	if (pos < 32) {
+		LO_WORD(*mask) = (1 << pos) - 1;
+		return;
+	}
+	LO_WORD(*mask) = -1;
+	HI_WORD(*mask) = (1 << (pos - 32)) - 1;
+}
+
+extern inline void bset64(int pos, unsigned long long *dest)
+{
+	/* This conditional will be optimized away.  Thanks, GCC! */
+	if (pos < 32)
+		asm volatile ("bset %1,%0":"=m"
+			      (LO_WORD(*dest)):"id"(pos));
+	else
+		asm volatile ("bset %1,%0":"=m"
+			      (HI_WORD(*dest)):"id"(pos - 32));
+}
+
+extern inline int btst64(int pos, unsigned long long dest)
+{
+	if (pos < 32)
+		return (0 != (LO_WORD(dest) & (1 << pos)));
+	else
+		return (0 != (HI_WORD(dest) & (1 << (pos - 32))));
+}
+
+extern inline void lsl64(int count, unsigned long long *dest)
+{
+	if (count < 32) {
+		HI_WORD(*dest) = (HI_WORD(*dest) << count)
+		    | (LO_WORD(*dest) >> count);
+		LO_WORD(*dest) <<= count;
+		return;
+	}
+	count -= 32;
+	HI_WORD(*dest) = LO_WORD(*dest) << count;
+	LO_WORD(*dest) = 0;
+}
+
+extern inline void lsr64(int count, unsigned long long *dest)
+{
+	if (count < 32) {
+		LO_WORD(*dest) = (LO_WORD(*dest) >> count)
+		    | (HI_WORD(*dest) << (32 - count));
+		HI_WORD(*dest) >>= count;
+		return;
+	}
+	count -= 32;
+	LO_WORD(*dest) = HI_WORD(*dest) >> count;
+	HI_WORD(*dest) = 0;
+}
+#endif
+
+extern inline void fp_denormalize(struct fp_ext *reg, unsigned int cnt)
+{
+	reg->exp += cnt;
+
+	switch (cnt) {
+	case 0 ... 8:
+		reg->lowmant = reg->mant.m32[1] << (8 - cnt);
+		reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) |
+				   (reg->mant.m32[0] << (32 - cnt));
+		reg->mant.m32[0] = reg->mant.m32[0] >> cnt;
+		break;
+	case 9 ... 32:
+		reg->lowmant = reg->mant.m32[1] >> (cnt - 8);
+		if (reg->mant.m32[1] << (40 - cnt))
+			reg->lowmant |= 1;
+		reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) |
+				   (reg->mant.m32[0] << (32 - cnt));
+		reg->mant.m32[0] = reg->mant.m32[0] >> cnt;
+		break;
+	case 33 ... 39:
+		asm volatile ("bfextu %1{%2,#8},%0" : "=d" (reg->lowmant)
+			: "m" (reg->mant.m32[0]), "d" (64 - cnt));
+		if (reg->mant.m32[1] << (40 - cnt))
+			reg->lowmant |= 1;
+		reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32);
+		reg->mant.m32[0] = 0;
+		break;
+	case 40 ... 71:
+		reg->lowmant = reg->mant.m32[0] >> (cnt - 40);
+		if ((reg->mant.m32[0] << (72 - cnt)) || reg->mant.m32[1])
+			reg->lowmant |= 1;
+		reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32);
+		reg->mant.m32[0] = 0;
+		break;
+	default:
+		reg->lowmant = reg->mant.m32[0] || reg->mant.m32[1];
+		reg->mant.m32[0] = 0;
+		reg->mant.m32[1] = 0;
+		break;
+	}
+}
+
+extern inline int fp_overnormalize(struct fp_ext *reg)
+{
+	int shift;
+
+	if (reg->mant.m32[0]) {
+		asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[0]));
+		reg->mant.m32[0] = (reg->mant.m32[0] << shift) | (reg->mant.m32[1] >> (32 - shift));
+		reg->mant.m32[1] = (reg->mant.m32[1] << shift);
+	} else {
+		asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[1]));
+		reg->mant.m32[0] = (reg->mant.m32[1] << shift);
+		reg->mant.m32[1] = 0;
+		shift += 32;
+	}
+
+	return shift;
+}
+
+extern inline int fp_addmant(struct fp_ext *dest, struct fp_ext *src)
+{
+	int carry;
+
+	/* we assume here, gcc only insert move and a clr instr */
+	asm volatile ("add.b %1,%0" : "=d,g" (dest->lowmant)
+		: "g,d" (src->lowmant), "0,0" (dest->lowmant));
+	asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[1])
+		: "d" (src->mant.m32[1]), "0" (dest->mant.m32[1]));
+	asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[0])
+		: "d" (src->mant.m32[0]), "0" (dest->mant.m32[0]));
+	asm volatile ("addx.l %0,%0" : "=d" (carry) : "0" (0));
+
+	return carry;
+}
+
+extern inline int fp_addcarry(struct fp_ext *reg)
+{
+	if (++reg->exp == 0x7fff) {
+		if (reg->mant.m64)
+			fp_set_sr(FPSR_EXC_INEX2);
+		reg->mant.m64 = 0;
+		fp_set_sr(FPSR_EXC_OVFL);
+		return 0;
+	}
+	reg->lowmant = (reg->mant.m32[1] << 7) | (reg->lowmant ? 1 : 0);
+	reg->mant.m32[1] = (reg->mant.m32[1] >> 1) |
+			   (reg->mant.m32[0] << 31);
+	reg->mant.m32[0] = (reg->mant.m32[0] >> 1) | 0x80000000;
+
+	return 1;
+}
+
+extern inline void fp_submant(struct fp_ext *dest, struct fp_ext *src1, struct fp_ext *src2)
+{
+	/* we assume here, gcc only insert move and a clr instr */
+	asm volatile ("sub.b %1,%0" : "=d,g" (dest->lowmant)
+		: "g,d" (src2->lowmant), "0,0" (src1->lowmant));
+	asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[1])
+		: "d" (src2->mant.m32[1]), "0" (src1->mant.m32[1]));
+	asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[0])
+		: "d" (src2->mant.m32[0]), "0" (src1->mant.m32[0]));
+}
+
+#define fp_mul64(desth, destl, src1, src2) ({				\
+	asm ("mulu.l %2,%1:%0" : "=d" (destl), "=d" (desth)		\
+		: "g" (src1), "0" (src2));				\
+})
+#define fp_div64(quot, rem, srch, srcl, div)				\
+	asm ("divu.l %2,%1:%0" : "=d" (quot), "=d" (rem)		\
+		: "dm" (div), "1" (srch), "0" (srcl))
+#define fp_add64(dest1, dest2, src1, src2) ({				\
+	asm ("add.l %1,%0" : "=d,dm" (dest2)				\
+		: "dm,d" (src2), "0,0" (dest2));			\
+	asm ("addx.l %1,%0" : "=d" (dest1)				\
+		: "d" (src1), "0" (dest1));				\
+})
+#define fp_addx96(dest, src) ({						\
+	/* we assume here, gcc only insert move and a clr instr */	\
+	asm volatile ("add.l %1,%0" : "=d,g" (dest->m32[2])		\
+		: "g,d" (temp.m32[1]), "0,0" (dest->m32[2]));		\
+	asm volatile ("addx.l %1,%0" : "=d" (dest->m32[1])		\
+		: "d" (temp.m32[0]), "0" (dest->m32[1]));		\
+	asm volatile ("addx.l %1,%0" : "=d" (dest->m32[0])		\
+		: "d" (0), "0" (dest->m32[0]));				\
+})
+#define fp_sub64(dest, src) ({						\
+	asm ("sub.l %1,%0" : "=d,dm" (dest.m32[1])			\
+		: "dm,d" (src.m32[1]), "0,0" (dest.m32[1]));		\
+	asm ("subx.l %1,%0" : "=d" (dest.m32[0])			\
+		: "d" (src.m32[0]), "0" (dest.m32[0]));			\
+})
+#define fp_sub96c(dest, srch, srcm, srcl) ({				\
+	char carry;							\
+	asm ("sub.l %1,%0" : "=d,dm" (dest.m32[2])			\
+		: "dm,d" (srcl), "0,0" (dest.m32[2]));			\
+	asm ("subx.l %1,%0" : "=d" (dest.m32[1])			\
+		: "d" (srcm), "0" (dest.m32[1]));			\
+	asm ("subx.l %2,%1; scs %0" : "=d" (carry), "=d" (dest.m32[0])	\
+		: "d" (srch), "1" (dest.m32[0]));			\
+	carry;								\
+})
+
+extern inline void fp_multiplymant(union fp_mant128 *dest, struct fp_ext *src1, struct fp_ext *src2)
+{
+	union fp_mant64 temp;
+
+	fp_mul64(dest->m32[0], dest->m32[1], src1->mant.m32[0], src2->mant.m32[0]);
+	fp_mul64(dest->m32[2], dest->m32[3], src1->mant.m32[1], src2->mant.m32[1]);
+
+	fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[0], src2->mant.m32[1]);
+	fp_addx96(dest, temp);
+
+	fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[1], src2->mant.m32[0]);
+	fp_addx96(dest, temp);
+}
+
+extern inline void fp_dividemant(union fp_mant128 *dest, struct fp_ext *src, struct fp_ext *div)
+{
+	union fp_mant128 tmp;
+	union fp_mant64 tmp64;
+	unsigned long *mantp = dest->m32;
+	unsigned long fix, rem, first, dummy;
+	int i;
+
+	/* the algorithm below requires dest to be smaller than div,
+	   but both have the high bit set */
+	if (src->mant.m64 >= div->mant.m64) {
+		fp_sub64(src->mant, div->mant);
+		*mantp = 1;
+	} else
+		*mantp = 0;
+	mantp++;
+
+	/* basic idea behind this algorithm: we can't divide two 64bit numbers
+	   (AB/CD) directly, but we can calculate AB/C0, but this means this
+	   quotient is off by C0/CD, so we have to multiply the first result
+	   to fix the result, after that we have nearly the correct result
+	   and only a few corrections are needed. */
+
+	/* C0/CD can be precalculated, but it's an 64bit division again, but
+	   we can make it a bit easier, by dividing first through C so we get
+	   10/1D and now only a single shift and the value fits into 32bit. */
+	fix = 0x80000000;
+	dummy = div->mant.m32[1] / div->mant.m32[0] + 1;
+	dummy = (dummy >> 1) | fix;
+	fp_div64(fix, dummy, fix, 0, dummy);
+	fix--;
+
+	for (i = 0; i < 3; i++, mantp++) {
+		if (src->mant.m32[0] == div->mant.m32[0]) {
+			fp_div64(first, rem, 0, src->mant.m32[1], div->mant.m32[0]);
+
+			fp_mul64(*mantp, dummy, first, fix);
+			*mantp += fix;
+		} else {
+			fp_div64(first, rem, src->mant.m32[0], src->mant.m32[1], div->mant.m32[0]);
+
+			fp_mul64(*mantp, dummy, first, fix);
+		}
+
+		fp_mul64(tmp.m32[0], tmp.m32[1], div->mant.m32[0], first - *mantp);
+		fp_add64(tmp.m32[0], tmp.m32[1], 0, rem);
+		tmp.m32[2] = 0;
+
+		fp_mul64(tmp64.m32[0], tmp64.m32[1], *mantp, div->mant.m32[1]);
+		fp_sub96c(tmp, 0, tmp64.m32[0], tmp64.m32[1]);
+
+		src->mant.m32[0] = tmp.m32[1];
+		src->mant.m32[1] = tmp.m32[2];
+
+		while (!fp_sub96c(tmp, 0, div->mant.m32[0], div->mant.m32[1])) {
+			src->mant.m32[0] = tmp.m32[1];
+			src->mant.m32[1] = tmp.m32[2];
+			*mantp += 1;
+		}
+	}
+}
+
+#if 0
+extern inline unsigned int fp_fls128(union fp_mant128 *src)
+{
+	unsigned long data;
+	unsigned int res, off;
+
+	if ((data = src->m32[0]))
+		off = 0;
+	else if ((data = src->m32[1]))
+		off = 32;
+	else if ((data = src->m32[2]))
+		off = 64;
+	else if ((data = src->m32[3]))
+		off = 96;
+	else
+		return 128;
+
+	asm ("bfffo %1{#0,#32},%0" : "=d" (res) : "dm" (data));
+	return res + off;
+}
+
+extern inline void fp_shiftmant128(union fp_mant128 *src, int shift)
+{
+	unsigned long sticky;
+
+	switch (shift) {
+	case 0:
+		return;
+	case 1:
+		asm volatile ("lsl.l #1,%0"
+			: "=d" (src->m32[3]) : "0" (src->m32[3]));
+		asm volatile ("roxl.l #1,%0"
+			: "=d" (src->m32[2]) : "0" (src->m32[2]));
+		asm volatile ("roxl.l #1,%0"
+			: "=d" (src->m32[1]) : "0" (src->m32[1]));
+		asm volatile ("roxl.l #1,%0"
+			: "=d" (src->m32[0]) : "0" (src->m32[0]));
+		return;
+	case 2 ... 31:
+		src->m32[0] = (src->m32[0] << shift) | (src->m32[1] >> (32 - shift));
+		src->m32[1] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift));
+		src->m32[2] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
+		src->m32[3] = (src->m32[3] << shift);
+		return;
+	case 32 ... 63:
+		shift -= 32;
+		src->m32[0] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift));
+		src->m32[1] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
+		src->m32[2] = (src->m32[3] << shift);
+		src->m32[3] = 0;
+		return;
+	case 64 ... 95:
+		shift -= 64;
+		src->m32[0] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
+		src->m32[1] = (src->m32[3] << shift);
+		src->m32[2] = src->m32[3] = 0;
+		return;
+	case 96 ... 127:
+		shift -= 96;
+		src->m32[0] = (src->m32[3] << shift);
+		src->m32[1] = src->m32[2] = src->m32[3] = 0;
+		return;
+	case -31 ... -1:
+		shift = -shift;
+		sticky = 0;
+		if (src->m32[3] << (32 - shift))
+			sticky = 1;
+		src->m32[3] = (src->m32[3] >> shift) | (src->m32[2] << (32 - shift)) | sticky;
+		src->m32[2] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift));
+		src->m32[1] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift));
+		src->m32[0] = (src->m32[0] >> shift);
+		return;
+	case -63 ... -32:
+		shift = -shift - 32;
+		sticky = 0;
+		if ((src->m32[2] << (32 - shift)) || src->m32[3])
+			sticky = 1;
+		src->m32[3] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)) | sticky;
+		src->m32[2] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift));
+		src->m32[1] = (src->m32[0] >> shift);
+		src->m32[0] = 0;
+		return;
+	case -95 ... -64:
+		shift = -shift - 64;
+		sticky = 0;
+		if ((src->m32[1] << (32 - shift)) || src->m32[2] || src->m32[3])
+			sticky = 1;
+		src->m32[3] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)) | sticky;
+		src->m32[2] = (src->m32[0] >> shift);
+		src->m32[1] = src->m32[0] = 0;
+		return;
+	case -127 ... -96:
+		shift = -shift - 96;
+		sticky = 0;
+		if ((src->m32[0] << (32 - shift)) || src->m32[1] || src->m32[2] || src->m32[3])
+			sticky = 1;
+		src->m32[3] = (src->m32[0] >> shift) | sticky;
+		src->m32[2] = src->m32[1] = src->m32[0] = 0;
+		return;
+	}
+
+	if (shift < 0 && (src->m32[0] || src->m32[1] || src->m32[2] || src->m32[3]))
+		src->m32[3] = 1;
+	else
+		src->m32[3] = 0;
+	src->m32[2] = 0;
+	src->m32[1] = 0;
+	src->m32[0] = 0;
+}
+#endif
+
+extern inline void fp_putmant128(struct fp_ext *dest, union fp_mant128 *src, int shift)
+{
+	unsigned long tmp;
+
+	switch (shift) {
+	case 0:
+		dest->mant.m64 = src->m64[0];
+		dest->lowmant = src->m32[2] >> 24;
+		if (src->m32[3] || (src->m32[2] << 8))
+			dest->lowmant |= 1;
+		break;
+	case 1:
+		asm volatile ("lsl.l #1,%0"
+			: "=d" (tmp) : "0" (src->m32[2]));
+		asm volatile ("roxl.l #1,%0"
+			: "=d" (dest->mant.m32[1]) : "0" (src->m32[1]));
+		asm volatile ("roxl.l #1,%0"
+			: "=d" (dest->mant.m32[0]) : "0" (src->m32[0]));
+		dest->lowmant = tmp >> 24;
+		if (src->m32[3] || (tmp << 8))
+			dest->lowmant |= 1;
+		break;
+	case 31:
+		asm volatile ("lsr.l #1,%1; roxr.l #1,%0"
+			: "=d" (dest->mant.m32[0])
+			: "d" (src->m32[0]), "0" (src->m32[1]));
+		asm volatile ("roxr.l #1,%0"
+			: "=d" (dest->mant.m32[1]) : "0" (src->m32[2]));
+		asm volatile ("roxr.l #1,%0"
+			: "=d" (tmp) : "0" (src->m32[3]));
+		dest->lowmant = tmp >> 24;
+		if (src->m32[3] << 7)
+			dest->lowmant |= 1;
+		break;
+	case 32:
+		dest->mant.m32[0] = src->m32[1];
+		dest->mant.m32[1] = src->m32[2];
+		dest->lowmant = src->m32[3] >> 24;
+		if (src->m32[3] << 8)
+			dest->lowmant |= 1;
+		break;
+	}
+}
+
+#if 0 /* old code... */
+extern inline int fls(unsigned int a)
+{
+	int r;
+
+	asm volatile ("bfffo %1{#0,#32},%0"
+		      : "=d" (r) : "md" (a));
+	return r;
+}
+
+/* fls = "find last set" (cf. ffs(3)) */
+extern inline int fls128(const int128 a)
+{
+	if (a[MSW128])
+		return fls(a[MSW128]);
+	if (a[NMSW128])
+		return fls(a[NMSW128]) + 32;
+	/* XXX: it probably never gets beyond this point in actual
+	   use, but that's indicative of a more general problem in the
+	   algorithm (i.e. as per the actual 68881 implementation, we
+	   really only need at most 67 bits of precision [plus
+	   overflow]) so I'm not going to fix it. */
+	if (a[NLSW128])
+		return fls(a[NLSW128]) + 64;
+	if (a[LSW128])
+		return fls(a[LSW128]) + 96;
+	else
+		return -1;
+}
+
+extern inline int zerop128(const int128 a)
+{
+	return !(a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]);
+}
+
+extern inline int nonzerop128(const int128 a)
+{
+	return (a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]);
+}
+
+/* Addition and subtraction */
+/* Do these in "pure" assembly, because "extended" asm is unmanageable
+   here */
+extern inline void add128(const int128 a, int128 b)
+{
+	/* rotating carry flags */
+	unsigned int carry[2];
+
+	carry[0] = a[LSW128] > (0xffffffff - b[LSW128]);
+	b[LSW128] += a[LSW128];
+
+	carry[1] = a[NLSW128] > (0xffffffff - b[NLSW128] - carry[0]);
+	b[NLSW128] = a[NLSW128] + b[NLSW128] + carry[0];
+
+	carry[0] = a[NMSW128] > (0xffffffff - b[NMSW128] - carry[1]);
+	b[NMSW128] = a[NMSW128] + b[NMSW128] + carry[1];
+
+	b[MSW128] = a[MSW128] + b[MSW128] + carry[0];
+}
+
+/* Note: assembler semantics: "b -= a" */
+extern inline void sub128(const int128 a, int128 b)
+{
+	/* rotating borrow flags */
+	unsigned int borrow[2];
+
+	borrow[0] = b[LSW128] < a[LSW128];
+	b[LSW128] -= a[LSW128];
+
+	borrow[1] = b[NLSW128] < a[NLSW128] + borrow[0];
+	b[NLSW128] = b[NLSW128] - a[NLSW128] - borrow[0];
+
+	borrow[0] = b[NMSW128] < a[NMSW128] + borrow[1];
+	b[NMSW128] = b[NMSW128] - a[NMSW128] - borrow[1];
+
+	b[MSW128] = b[MSW128] - a[MSW128] - borrow[0];
+}
+
+/* Poor man's 64-bit expanding multiply */
+extern inline void mul64(unsigned long long a,
+		  unsigned long long b,
+		  int128 c)
+{
+	unsigned long long acc;
+	int128 acc128;
+
+	zero128(acc128);
+	zero128(c);
+
+	/* first the low words */
+	if (LO_WORD(a) && LO_WORD(b)) {
+		acc = (long long) LO_WORD(a) * LO_WORD(b);
+		c[NLSW128] = HI_WORD(acc);
+		c[LSW128] = LO_WORD(acc);
+	}
+	/* Next the high words */
+	if (HI_WORD(a) && HI_WORD(b)) {
+		acc = (long long) HI_WORD(a) * HI_WORD(b);
+		c[MSW128] = HI_WORD(acc);
+		c[NMSW128] = LO_WORD(acc);
+	}
+	/* The middle words */
+	if (LO_WORD(a) && HI_WORD(b)) {
+		acc = (long long) LO_WORD(a) * HI_WORD(b);
+		acc128[NMSW128] = HI_WORD(acc);
+		acc128[NLSW128] = LO_WORD(acc);
+		add128(acc128, c);
+	}
+	/* The first and last words */
+	if (HI_WORD(a) && LO_WORD(b)) {
+		acc = (long long) HI_WORD(a) * LO_WORD(b);
+		acc128[NMSW128] = HI_WORD(acc);
+		acc128[NLSW128] = LO_WORD(acc);
+		add128(acc128, c);
+	}
+}
+
+/* Note: unsigned */
+extern inline int cmp128(int128 a, int128 b)
+{
+	if (a[MSW128] < b[MSW128])
+		return -1;
+	if (a[MSW128] > b[MSW128])
+		return 1;
+	if (a[NMSW128] < b[NMSW128])
+		return -1;
+	if (a[NMSW128] > b[NMSW128])
+		return 1;
+	if (a[NLSW128] < b[NLSW128])
+		return -1;
+	if (a[NLSW128] > b[NLSW128])
+		return 1;
+
+	return (signed) a[LSW128] - b[LSW128];
+}
+
+inline void div128(int128 a, int128 b, int128 c)
+{
+	int128 mask;
+
+	/* Algorithm:
+
+	   Shift the divisor until it's at least as big as the
+	   dividend, keeping track of the position to which we've
+	   shifted it, i.e. the power of 2 which we've multiplied it
+	   by.
+
+	   Then, for this power of 2 (the mask), and every one smaller
+	   than it, subtract the mask from the dividend and add it to
+	   the quotient until the dividend is smaller than the raised
+	   divisor.  At this point, divide the dividend and the mask
+	   by 2 (i.e. shift one place to the right).  Lather, rinse,
+	   and repeat, until there are no more powers of 2 left. */
+
+	/* FIXME: needless to say, there's room for improvement here too. */
+
+	/* Shift up */
+	/* XXX: since it just has to be "at least as big", we can
+	   probably eliminate this horribly wasteful loop.  I will
+	   have to prove this first, though */
+	set128(0, 0, 0, 1, mask);
+	while (cmp128(b, a) < 0 && !btsthi128(b)) {
+		lslone128(b);
+		lslone128(mask);
+	}
+
+	/* Shift down */
+	zero128(c);
+	do {
+		if (cmp128(a, b) >= 0) {
+			sub128(b, a);
+			add128(mask, c);
+		}
+		lsrone128(mask);
+		lsrone128(b);
+	} while (nonzerop128(mask));
+
+	/* The remainder is in a... */
+}
+#endif
+
+#endif	/* MULTI_ARITH_H */
diff -urN linux-2.2.18-pre15/arch/m68k/mm/fault.c linux/arch/m68k/mm/fault.c
--- linux-2.2.18-pre15/arch/m68k/mm/fault.c	Thu Dec 17 09:06:30 1998
+++ linux/arch/m68k/mm/fault.c	Mon Dec  3 20:33:54 2001
@@ -19,6 +19,48 @@
 extern void die_if_kernel(char *, struct pt_regs *, long);
 extern const int frame_extra_sizes[]; /* in m68k/kernel/signal.c */
 
+int send_fault_sig(struct pt_regs *regs)
+{
+	if (user_mode(regs)) {
+		force_sig_info(current->buserr_info.si_signo,
+			       &current->buserr_info, current);
+	} else {
+		unsigned long fixup;
+
+		/* Are we prepared to handle this kernel fault? */
+		if ((fixup = search_exception_table(regs->pc))) {
+			struct pt_regs *tregs;
+			/* Create a new four word stack frame, discarding the old
+			   one.  */
+			regs->stkadj = frame_extra_sizes[regs->format];
+			tregs =	(struct pt_regs *)((ulong)regs + regs->stkadj);
+			tregs->vector = regs->vector;
+			tregs->format = 0;
+			tregs->pc = fixup;
+			tregs->sr = regs->sr;
+			return -1;
+		}
+
+		if (current->buserr_info.si_signo == SIGBUS)
+			force_sig_info(current->buserr_info.si_signo,
+				       &current->buserr_info, current);
+
+		/*
+		 * Oops. The kernel tried to access some bad page. We'll have to
+		 * terminate things with extreme prejudice.
+		 */
+		if ((unsigned long)current->buserr_info.si_addr < PAGE_SIZE)
+			printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+		else
+			printk(KERN_ALERT "Unable to handle kernel access");
+		printk(" at virtual address %p\n", current->buserr_info.si_addr);
+		die_if_kernel("Oops", regs, 0 /*error_code*/);
+		do_exit(SIGKILL);
+	}
+
+	return 1;
+}
+
 /*
  * This routine handles page faults.  It determines the problem, and
  * then passes it off to one of the appropriate routines.
@@ -30,12 +72,11 @@
  * If this routine detects a bad access, it returns 1, otherwise it
  * returns 0.
  */
-asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
-			      unsigned long error_code)
+int do_page_fault(struct pt_regs *regs, unsigned long address,
+		  unsigned long error_code)
 {
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct * vma;
-	unsigned long fixup;
 	int write;
 
 #ifdef DEBUG
@@ -56,23 +97,23 @@
 
 	vma = find_vma(mm, address);
 	if (!vma)
-		goto bad_area;
+		goto map_err;
 	if (vma->vm_flags & VM_IO)
-		goto bad_area;
+		goto acc_err;
 	if (vma->vm_start <= address)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
-		goto bad_area;
+		goto map_err;
 	if (user_mode(regs)) {
 		/* Accessing the stack below usp is always a bug.  The
 		   "+ 256" is there due to some instructions doing
 		   pre-decrement on the stack and that doesn't show up
 		   until later.  */
 		if (address + 256 < rdusp())
-			goto bad_area;
+			goto map_err;
 	}
 	if (expand_stack(vma, address))
-		goto bad_area;
+		goto map_err;
 
 /*
  * Ok, we have a good vm_area for this memory access, so
@@ -85,14 +126,14 @@
 			/* fall through */
 		case 2:		/* write, not present */
 			if (!(vma->vm_flags & VM_WRITE))
-				goto bad_area;
+				goto acc_err;
 			write++;
 			break;
 		case 1:		/* read, present */
-			goto bad_area;
+			goto acc_err;
 		case 0:		/* read, not present */
 			if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
-				goto bad_area;
+				goto acc_err;
 	}
 
 	/*
@@ -101,77 +142,42 @@
 	 * the fault.
 	 */
 	if (!handle_mm_fault(current, vma, address, write))
-		goto do_sigbus;
+		goto bus_err;
 
 	/* There seems to be a missing invalidate somewhere in do_no_page.
 	 * Until I found it, this one cures the problem and makes
 	 * 1.2 run on the 68040 (Martin Apel).
 	 */
+
 	if (CPU_IS_040_OR_060)
 		flush_tlb_page(vma, address);
-	up(&mm->mmap_sem);
-	return 0;
 
-/*
- * 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(&mm->mmap_sem);
-
-	/* User mode accesses just cause a SIGSEGV */
-	if (user_mode(regs)) {
-		siginfo_t info;
-		info.si_signo = SIGSEGV;
-		info.si_code = SEGV_MAPERR;
-		info.si_addr = (void *)address;
-		force_sig_info(SIGSEGV, &info, current);
-		return 1;
-	}
+	return 0;
 
 no_context:
-	/* Are we prepared to handle this kernel fault?  */
-	if ((fixup = search_exception_table(regs->pc)) != 0) {
-		struct pt_regs *tregs;
-		/* Create a new four word stack frame, discarding the old
-		   one.  */
-		regs->stkadj = frame_extra_sizes[regs->format];
-		tregs =	(struct pt_regs *)((ulong)regs + regs->stkadj);
-		tregs->vector = regs->vector;
-		tregs->format = 0;
-		tregs->pc = fixup;
-		tregs->sr = regs->sr;
-		return -1;
-	}
+	current->buserr_info.si_signo = SIGBUS;
+	current->buserr_info.si_addr = (void *)address;
+	return send_fault_sig(regs);
+
+bus_err:
+	current->buserr_info.si_signo = SIGBUS;
+	current->buserr_info.si_code = BUS_ADRERR;
+	current->buserr_info.si_addr = (void *)address;
+	goto send_sig;
+
+map_err:
+	current->buserr_info.si_signo = SIGSEGV;
+	current->buserr_info.si_code = SEGV_MAPERR;
+	current->buserr_info.si_addr = (void *)address;
+	goto send_sig;
+
+acc_err:
+	current->buserr_info.si_signo = SIGSEGV;
+	current->buserr_info.si_code = SEGV_ACCERR;
+	current->buserr_info.si_addr = (void *)address;
 
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- */
-	if ((unsigned long) address < PAGE_SIZE) {
-		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
-	} else
-		printk(KERN_ALERT "Unable to handle kernel access");
-	printk(" at virtual address %08lx\n",address);
-	die_if_kernel("Oops", regs, error_code);
-	do_exit(SIGKILL);
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-do_sigbus:
+send_sig:
 	up(&mm->mmap_sem);
-
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel
-	 * or user mode.
-	 */
-	force_sig(SIGBUS, current);
-
-	/* Kernel mode? Handle exceptions or die */
-	if (!user_mode(regs))
-		goto no_context;
-
-	return 1;
+	return send_fault_sig(regs);
 }
diff -urN linux-2.2.18-pre15/arch/m68k/mm/init.c linux/arch/m68k/mm/init.c
--- linux-2.2.18-pre15/arch/m68k/mm/init.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/mm/init.c	Mon Dec  3 20:33:54 2001
@@ -139,6 +139,7 @@
 }
 
 static pmd_t *last_pgtable __initdata = NULL;
+static pmd_t *zero_pgtable __initdata = NULL;
 
 __initfunc(static pmd_t * kernel_ptr_table(unsigned long *memavailp))
 {
@@ -236,7 +237,8 @@
 #ifdef DEBUG
 				printk ("[zero map]");
 #endif
-				pte_dir = (pte_t *)kernel_ptr_table(memavailp);
+				zero_pgtable = kernel_ptr_table(memavailp);
+				pte_dir = (pte_t *)zero_pgtable;
 				pmd_dir->pmd[0] = virt_to_phys(pte_dir) |
 					_PAGE_TABLE | _PAGE_ACCESSED;
 				pte_val(*pte_dir++) = 0;
@@ -380,7 +382,7 @@
 				  "pmove %0,%%crp\n\t"
 				  ".chip 68k"
 				  : /* no outputs */
-				  : "m" (task[0]->tss.crp[0]));
+				  : "m" (task[0]->tss.crp[1]|_PAGE_TABLE));
 #ifdef DEBUG
 	printk ("set crp\n");
 #endif
@@ -450,6 +452,9 @@
 		if (pgd_present(kernel_pg_dir[i]))
 			init_pointer_table(pgd_page(kernel_pg_dir[i]));
 	}
+	/* insert also pointer table that we used to unmap the zero page */
+	if (zero_pgtable)
+		init_pointer_table((unsigned long)zero_pgtable);
 
 	printk("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init)\n",
 	       (unsigned long) nr_free_pages << (PAGE_SHIFT-10),
diff -urN linux-2.2.18-pre15/arch/m68k/mm/kmap.c linux/arch/m68k/mm/kmap.c
--- linux-2.2.18-pre15/arch/m68k/mm/kmap.c	Wed May 12 08:50:08 1999
+++ linux/arch/m68k/mm/kmap.c	Mon Dec  3 20:33:54 2001
@@ -25,6 +25,7 @@
 #undef DEBUG
 
 #define PTRTREESIZE	(256*1024)
+#define ROOTTREESIZE (32*1024*1024)
 
 /*
  * For 040/060 we can use the virtual memory area like other architectures,
@@ -189,7 +190,20 @@
 			printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr);
 #endif
 		pgd_dir = pgd_offset_k(virtaddr);
-		pmd_dir = pmd_alloc_kernel(pgd_dir, virtaddr);
+		if (CPU_IS_020_OR_030) {
+			if (!(virtaddr & (ROOTTREESIZE-1)) &&
+			    size >= ROOTTREESIZE) {
+				pgd_val(*pgd_dir) = physaddr;
+				size -= ROOTTREESIZE;
+				virtaddr += ROOTTREESIZE;
+				physaddr += ROOTTREESIZE;
+				continue;
+			}
+		}
+		if (!pgd_present(*pgd_dir))
+			pmd_dir = pmd_alloc_kernel(pgd_dir, virtaddr);
+		else
+			pmd_dir = pmd_offset(pgd_dir, virtaddr);
 		if (!pmd_dir) {
 			printk("ioremap: no mem for pmd_dir\n");
 			return NULL;
@@ -201,7 +215,10 @@
 			virtaddr += PTRTREESIZE;
 			size -= PTRTREESIZE;
 		} else {
-			pte_dir = pte_alloc_kernel(pmd_dir, virtaddr);
+			if (!pmd_present(*pmd_dir))
+				pte_dir = pte_alloc_kernel(pmd_dir, virtaddr);
+			else
+				pte_dir = pte_offset(pmd_dir, virtaddr);
 			if (!pte_dir) {
 				printk("ioremap: no mem for pte_dir\n");
 				return NULL;
diff -urN linux-2.2.18-pre15/arch/m68k/mvme147/config.c linux/arch/m68k/mvme147/config.c
--- linux-2.2.18-pre15/arch/m68k/mvme147/config.c	Wed May 12 08:50:08 1999
+++ linux/arch/m68k/mvme147/config.c	Mon Dec  3 20:33:54 2001
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 
+#include <asm/bootinfo.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/setup.h>
@@ -62,6 +63,14 @@
 
 void (*tick_handler)(int, void *, struct pt_regs *);
 
+
+int mvme147_parse_bootinfo(const struct bi_record *bi)
+{
+	if (bi->tag == BI_VME_TYPE || bi->tag == BI_VME_BRDINFO)
+		return 0;
+	else
+		return 1;
+}
 
 int mvme147_kbdrate (struct kbd_repeat *k)
 {
diff -urN linux-2.2.18-pre15/arch/m68k/mvme16x/Makefile linux/arch/m68k/mvme16x/Makefile
--- linux-2.2.18-pre15/arch/m68k/mvme16x/Makefile	Sat Jun 13 13:14:33 1998
+++ linux/arch/m68k/mvme16x/Makefile	Mon Dec  3 20:33:54 2001
@@ -9,6 +9,6 @@
 
 O_TARGET := mvme16x.o
 O_OBJS   := config.o 16xints.o rtc.o
-#OX_OBJS = ksyms.o
+OX_OBJS  := mvme16x_ksyms.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre15/arch/m68k/mvme16x/config.c linux/arch/m68k/mvme16x/config.c
--- linux-2.2.18-pre15/arch/m68k/mvme16x/config.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/mvme16x/config.c	Mon Dec  3 20:33:54 2001
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 
+#include <asm/bootinfo.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/setup.h>
@@ -36,6 +37,7 @@
 
 int atari_SCC_reset_done = 1;		/* So SCC doesn't get reset */
 u_long atari_mch_cookie = 0;
+extern unsigned long mvme_bdid;
 
 static MK48T08ptr_t volatile rtc = (MK48T08ptr_t)MVME_RTC_BASE;
 
@@ -72,6 +74,14 @@
 unsigned short mvme16x_config;
 
 
+int mvme16x_parse_bootinfo(const struct bi_record *bi)
+{
+	if (bi->tag == BI_VME_TYPE || bi->tag == BI_VME_BRDINFO)
+		return 0;
+	else
+		return 1;
+}
+
 int mvme16x_kbdrate (struct kbd_repeat *k)
 {
 	return 0;
@@ -92,7 +102,7 @@
 
 static void mvme16x_get_model(char *model)
 {
-    p_bdid p = (p_bdid)mvme_bdid_ptr;
+    p_bdid p = (p_bdid)&mvme_bdid;
     char suf[4];
 
     suf[1] = p->brdsuffix[0];
@@ -106,7 +116,7 @@
 
 static int mvme16x_get_hardware_list(char *buffer)
 {
-    p_bdid p = (p_bdid)mvme_bdid_ptr;
+    p_bdid p = (p_bdid)&mvme_bdid;
     int len = 0;
 
     if (p->brdno == 0x0162 || p->brdno == 0x0172)
@@ -134,7 +144,7 @@
 
 __initfunc(void config_mvme16x(void))
 {
-    p_bdid p = (p_bdid)mvme_bdid_ptr;
+    p_bdid p = (p_bdid)&mvme_bdid;
     char id[40];
 
     mach_sched_init      = mvme16x_sched_init;
@@ -200,7 +210,7 @@
 
 static void mvme16x_abort_int (int irq, void *dev_id, struct pt_regs *fp)
 {
-	p_bdid p = (p_bdid)mvme_bdid_ptr;
+	p_bdid p = (p_bdid)&mvme_bdid;
 	unsigned long *new = (unsigned long *)vectors;
 	unsigned long *old = (unsigned long *)0xffe00000;
 	volatile unsigned char uc, *ucp;
@@ -233,7 +243,7 @@
 
 void mvme16x_sched_init (void (*timer_routine)(int, void *, struct pt_regs *))
 {
-    p_bdid p = (p_bdid)mvme_bdid_ptr;
+    p_bdid p = (p_bdid)&mvme_bdid;
     int irq;
 
     tick_handler = timer_routine;
@@ -299,19 +309,22 @@
 
 extern void mvme167_serial_console_setup(int cflag);
 extern void serial167_write(struct console *co, const char *str, unsigned cnt);
+extern int serial167_wait_key(struct console *co);
 extern void vme_scc_write(struct console *co, const char *str, unsigned cnt);
+extern int vme_scc_wait_key(struct console *co);
 
 
 void mvme16x_init_console_port (struct console *co, int cflag)
 {
-	p_bdid p = (p_bdid)mvme_bdid_ptr;
+	p_bdid p = (p_bdid)&mvme_bdid;
 
 	switch (p->brdno)
 	{
 #ifdef CONFIG_MVME162_SCC
 	case 0x0162:
 	case 0x0172:
-		co->write = vme_scc_write;
+		co->write    = vme_scc_write;
+		co->wait_key = vme_scc_wait_key;
 		return;
 #endif
 #ifdef CONFIG_SERIAL167
@@ -319,7 +332,8 @@
 	case 0x0167:
 	case 0x0176:
 	case 0x0177:
-		co->write = serial167_write;
+		co->write    = serial167_write;
+		co->wait_key = serial167_wait_key;
 		mvme167_serial_console_setup (cflag);
 		return;
 #endif
@@ -371,4 +385,27 @@
 	}
 	restore_flags(flags);
 }
+
+
+int vme_scc_wait_key (struct console *co)
+{
+	volatile unsigned char *p = (volatile char *)MVME_SCC_A_ADDR;
+	unsigned long	flags;
+	int		c;
+
+	/* wait for rx buf filled */
+	while ((*p & 0x01) == 0)
+		;
+
+	save_flags(flags);
+	cli();
+
+	*p = 8;
+	scc_delay();
+	c = *p;
+
+	restore_flags(flags);
+	return c;
+}
+
 #endif
diff -urN linux-2.2.18-pre15/arch/m68k/mvme16x/mvme16x_ksyms.c linux/arch/m68k/mvme16x/mvme16x_ksyms.c
--- linux-2.2.18-pre15/arch/m68k/mvme16x/mvme16x_ksyms.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/mvme16x/mvme16x_ksyms.c	Mon Dec  3 20:33:54 2001
@@ -0,0 +1,6 @@
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/ptrace.h>
+#include <asm/mvme16xhw.h>
+ 
+EXPORT_SYMBOL(mvme16x_config);
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/chrp_setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/chrp_setup.c	Mon Dec  3 20:33:54 2001
@@ -33,6 +33,7 @@
 #include <linux/pci.h>
 #include <linux/openpic.h>
 #include <linux/version.h>
+#include <linux/delay.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -94,7 +95,6 @@
 extern PTE *Hash, *Hash_end;
 extern unsigned long Hash_size, Hash_mask;
 extern int probingmem;
-extern unsigned long loops_per_sec;
 
 unsigned long empty_zero_page[1024];
 
@@ -240,7 +240,7 @@
 	struct device_node *device;
 
 	/* init to some ~sane value until calibrate_delay() runs */
-	loops_per_sec = 50000000;
+	loops_per_jiffy = 50000000/HZ;
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* this is fine for chrp */
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/gemini_setup.c linux/arch/ppc/kernel/gemini_setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/gemini_setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/gemini_setup.c	Mon Dec  3 20:33:54 2001
@@ -21,6 +21,7 @@
 #include <linux/blk.h>
 #include <linux/console.h>
 #include <linux/openpic.h>
+#include <linux/delay.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
@@ -166,8 +167,6 @@
 	ioremap( GEMINI_MPIC_ADDR, sizeof( struct OpenPIC ));
 }
 
-
-extern unsigned long loops_per_sec;
 extern int root_mountflags;
 extern char cmd_line[];
 
@@ -179,7 +178,7 @@
 	extern char cmd_line[];
 
 
-	loops_per_sec = 50000000;
+	loops_per_jiffy = 50000000/HZ;
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* bootable off CDROM */
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/mbx_setup.c linux/arch/ppc/kernel/mbx_setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/mbx_setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/mbx_setup.c	Mon Dec  3 20:33:54 2001
@@ -31,6 +31,7 @@
 #include <linux/init.h>
 #include <linux/blk.h>
 #include <linux/ioport.h>
+#include <linux/delay.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -55,8 +56,6 @@
 extern char mackbd_unexpected_up(unsigned char keycode);
 extern void mackbd_leds(unsigned char leds);
 extern void mackbd_init_hw(void);
-
-extern unsigned long loops_per_sec;
 
 unsigned long empty_zero_page[1024];
 
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/pmac_setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/pmac_setup.c	Mon Dec  3 20:33:54 2001
@@ -260,13 +260,13 @@
 			case 10:	/* mach V (604ev5) */
 			case 12:	/* G4 */
 			case 20:	/* 620 */
-				loops_per_sec = *fp;
+				loops_per_jiffy = (*fp)/HZ;
 				break;
 			default:	/* 601, 603, etc. */
-				loops_per_sec = *fp / 2;
+				loops_per_jiffy = (*fp / 2)/HZ;
 			}
 		} else
-			loops_per_sec = 50000000;
+			loops_per_jiffy = 50000000/HZ;
 	}
 
 	/* this area has the CPU identification register
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/prep_setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/prep_setup.c	Mon Dec  3 20:33:54 2001
@@ -33,6 +33,7 @@
 #include <linux/timex.h>
 #include <linux/pci.h>
 #include <linux/openpic.h>
+#include <linux/delay.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -100,7 +101,6 @@
 extern PTE *Hash, *Hash_end;
 extern unsigned long Hash_size, Hash_mask;
 extern int probingmem;
-extern unsigned long loops_per_sec;
 
 #ifdef CONFIG_BLK_DEV_RAM
 extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
@@ -218,7 +218,7 @@
 	unsigned char ucEquipPres1;
 
 	/* init to some ~sane value until calibrate_delay() runs */
-	loops_per_sec = 50000000;
+	loops_per_jiffy = 50000000/HZ;
 	
 	/* Set up floppy in PS/2 mode */
 	outb(0x09, SIO_CONFIG_RA);
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- linux-2.2.18-pre15/arch/ppc/kernel/setup.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/setup.c	Mon Dec  3 20:33:54 2001
@@ -313,9 +313,9 @@
 			       (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff);
 
 		len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n",
-			       (CD(loops_per_sec)+2500)/500000,
-			       (CD(loops_per_sec)+2500)/5000 % 100);
-		bogosum += CD(loops_per_sec);
+			       (CD(loops_per_jiffy*HZ)+2500)/500000,
+			       (CD(loops_per_jiffy*HZ)+2500)/5000 % 100);
+		bogosum += CD(loops_per_jiffy);
 	}
 
 #ifdef __SMP__
@@ -713,7 +713,7 @@
 	id->word123        = __le16_to_cpu(id->word123);
 	id->word124        = __le16_to_cpu(id->word124);
 	id->word125        = __le16_to_cpu(id->word125);
-	id->word126        = __le16_to_cpu(id->word126);
+	id->last_lun       = __le16_to_cpu(id->last_lun);
 	id->word127        = __le16_to_cpu(id->word127);
 	id->security       = __le16_to_cpu(id->security);
 	for (i=0; i<127; i++)
diff -urN linux-2.2.18-pre15/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- linux-2.2.18-pre15/arch/ppc/kernel/smp.c	Mon Sep  4 10:39:16 2000
+++ linux/arch/ppc/kernel/smp.c	Mon Dec  3 20:33:54 2001
@@ -461,7 +461,7 @@
 {
         struct cpuinfo_PPC *c = &cpu_data[id];
 	/* assume bogomips are same for everything */
-        c->loops_per_sec = loops_per_sec;
+        c->loops_per_jiffy = loops_per_jiffy;
         c->pvr = _get_PVR();
 }
 
diff -urN linux-2.2.18-pre15/arch/sparc/defconfig linux/arch/sparc/defconfig
--- linux-2.2.18-pre15/arch/sparc/defconfig	Wed May  3 17:16:32 2000
+++ linux/arch/sparc/defconfig	Mon Dec  3 20:33:54 2001
@@ -93,6 +93,7 @@
 CONFIG_MD_MIRRORING=m
 CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_NBD=m
@@ -133,6 +134,7 @@
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
+# CONFIG_NET_DIVERT is not set
 # CONFIG_LLC is not set
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
@@ -262,7 +264,9 @@
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+CONFIG_NFS_V3=y
 CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
 CONFIG_NCP_FS=m
 # CONFIG_NCPFS_PACKET_SIGNING is not set
 # CONFIG_NCPFS_IOCTL_LOCKING is not set
@@ -287,6 +291,7 @@
 #
 # Native Language Support
 #
+CONFIG_NLS_DEFAULT="cp437"
 # CONFIG_NLS_CODEPAGE_437 is not set
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
@@ -303,6 +308,10 @@
 # CONFIG_NLS_CODEPAGE_866 is not set
 # CONFIG_NLS_CODEPAGE_869 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
 # CONFIG_NLS_ISO8859_1 is not set
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
diff -urN linux-2.2.18-pre15/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- linux-2.2.18-pre15/arch/sparc/kernel/entry.S	Wed May  3 17:16:32 2000
+++ linux/arch/sparc/kernel/entry.S	Mon Dec  3 20:33:54 2001
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.159.2.7 2000/01/21 01:05:35 davem Exp $
+/* $Id: entry.S,v 1.159.2.8 2000/10/05 04:17:17 anton Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1807,13 +1807,13 @@
 C_LABEL(udelay):
 	save	%sp, -REGWIN_SZ, %sp
 	mov	%i0, %o0
-	sethi	%hi(0x10c6), %o1
+	sethi	%hi(0x10c600), %o1
 	call	.umul
-	 or	%o1, %lo(0x10c6), %o1
+	 or	%o1, %lo(0x10c600), %o1
 #ifndef __SMP__
-	sethi	%hi(C_LABEL(loops_per_sec)), %o3
+	sethi	%hi(C_LABEL(loops_per_jiffy)), %o3
 	call	.umul
-	 ld	[%o3 + %lo(C_LABEL(loops_per_sec))], %o1
+	 ld	[%o3 + %lo(C_LABEL(loops_per_jiffy))], %o1
 #else
 	GET_PROCESSOR_OFFSET(o4, o2)
 	set	C_LABEL(cpu_data), %o3
diff -urN linux-2.2.18-pre15/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- linux-2.2.18-pre15/arch/sparc/kernel/setup.c	Tue Jan  4 10:12:12 2000
+++ linux/arch/sparc/kernel/setup.c	Mon Dec  3 20:33:54 2001
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.105.2.1 1999/11/16 06:29:31 davem Exp $
+/*  $Id: setup.c,v 1.105.2.2 2000/10/05 04:17:17 anton Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -531,7 +531,7 @@
             &cputypval,
 	    linux_num_cpus, smp_num_cpus
 #ifndef __SMP__
-	    , loops_per_sec/500000, (loops_per_sec/5000) % 100
+	    , loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100
 #endif
 	    );
 #ifdef __SMP__
diff -urN linux-2.2.18-pre15/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- linux-2.2.18-pre15/arch/sparc/kernel/smp.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/kernel/smp.c	Mon Dec  3 20:33:54 2001
@@ -14,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 
 #include <asm/ptrace.h>
 #include <asm/atomic.h>
@@ -85,7 +86,7 @@
 
 __initfunc(void smp_store_cpu_info(int id))
 {
-	cpu_data[id].udelay_val = loops_per_sec; /* this is it on sparc. */
+	cpu_data[id].udelay_val = loops_per_jiffy; /* this is it on sparc. */
 }
 
 __initfunc(void smp_commence(void))
@@ -288,8 +289,8 @@
 		if (cpu_present_map & (1 << i))
 			len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", 
 					i,
-					cpu_data[i].udelay_val/500000,
-					(cpu_data[i].udelay_val/5000)%100);
+					cpu_data[i].udelay_val/(500000/HZ),
+					(cpu_data[i].udelay_val/(5000/HZ))%100);
 	return len;
 }
 
diff -urN linux-2.2.18-pre15/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c
--- linux-2.2.18-pre15/arch/sparc/kernel/sun4d_smp.c	Tue Jan  4 10:12:12 2000
+++ linux/arch/sparc/kernel/sun4d_smp.c	Mon Dec  3 20:33:54 2001
@@ -278,11 +278,11 @@
 				smp_highest_cpu = i;
 			}
 		}
-		SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100));
+		SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, bogosum/(500000/HZ), (bogosum/(5000/HZ))%100));
 		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
 		       cpucount + 1,
-		       (bogosum + 2500)/500000,
-		       ((bogosum + 2500)/5000)%100);
+		       bogosum/(500000/HZ),
+		       (bogosum/(5000/HZ))%100);
 		smp_activated = 1;
 		smp_num_cpus = cpucount + 1;
 	}
diff -urN linux-2.2.18-pre15/arch/sparc/kernel/sun4m_smp.c linux/arch/sparc/kernel/sun4m_smp.c
--- linux-2.2.18-pre15/arch/sparc/kernel/sun4m_smp.c	Tue Jan  4 10:12:12 2000
+++ linux/arch/sparc/kernel/sun4m_smp.c	Mon Dec  3 20:33:54 2001
@@ -243,8 +243,8 @@
 		}
 		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
 		       cpucount + 1,
-		       (bogosum + 2500)/500000,
-		       ((bogosum + 2500)/5000)%100);
+		       bogosum/(500000/HZ),
+		       (bogosum/(5000/HZ))%100);
 		smp_activated = 1;
 		smp_num_cpus = cpucount + 1;
 	}
diff -urN linux-2.2.18-pre15/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- linux-2.2.18-pre15/arch/sparc64/defconfig	Mon Dec  3 20:33:28 2001
+++ linux/arch/sparc64/defconfig	Mon Dec  3 20:33:54 2001
@@ -100,6 +100,7 @@
 CONFIG_PRINTER=m
 CONFIG_PRINTER_READBACK=y
 CONFIG_ENVCTRL=m
+CONFIG_DISPLAY7SEG=m
 
 #
 # Floppy, IDE, and other block devices
@@ -111,6 +112,7 @@
 CONFIG_MD_MIRRORING=m
 CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_NBD=m
diff -urN linux-2.2.18-pre15/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- linux-2.2.18-pre15/arch/sparc64/kernel/setup.c	Mon Dec  3 20:33:28 2001
+++ linux/arch/sparc64/kernel/setup.c	Mon Dec  3 20:33:54 2001
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.43.2.4 2000/09/16 16:56:08 davem Exp $
+/*  $Id: setup.c,v 1.43.2.5 2000/10/02 02:05:37 anton Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -642,7 +642,7 @@
             prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff,
 	    linux_num_cpus, smp_num_cpus
 #ifndef __SMP__
-            , loops_per_sec/500000, (loops_per_sec/5000) % 100
+            , loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100
 #endif
 	    );
 #ifdef __SMP__
diff -urN linux-2.2.18-pre15/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- linux-2.2.18-pre15/arch/sparc64/kernel/smp.c	Wed May  3 17:16:32 2000
+++ linux/arch/sparc64/kernel/smp.c	Mon Dec  3 20:33:54 2001
@@ -78,8 +78,8 @@
 		if(cpu_present_map & (1UL << i))
 			len += sprintf(buf + len,
 				       "Cpu%dBogo\t: %lu.%02lu\n",
-				       i, cpu_data[i].udelay_val / 500000,
-				       (cpu_data[i].udelay_val / 5000) % 100);
+				       i, cpu_data[i].udelay_val / (500000/HZ),
+				       (cpu_data[i].udelay_val / (5000/HZ)) % 100);
 	return len;
 }
 
@@ -91,7 +91,7 @@
 	cpu_data[id].bh_count			= 0;
 	/* multiplier and counter set by
 	   smp_setup_percpu_timer()  */
-	cpu_data[id].udelay_val			= loops_per_sec;
+	cpu_data[id].udelay_val			= loops_per_jiffy;
 
 	cpu_data[id].pgcache_size		= 0;
 	cpu_data[id].pte_cache[0]		= NULL;
@@ -280,8 +280,8 @@
 		}
 		printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
 		       cpucount + 1,
-		       (bogosum + 2500)/500000,
-		       ((bogosum + 2500)/5000)%100);
+		       (bogosum + 2500)/(500000/HZ),
+		       ((bogosum + 2500)/(5000/HZ))%100);
 		smp_activated = 1;
 		smp_num_cpus = cpucount + 1;
 	}
diff -urN linux-2.2.18-pre15/drivers/Makefile linux/drivers/Makefile
--- linux-2.2.18-pre15/drivers/Makefile	Wed May  3 17:16:32 2000
+++ linux/drivers/Makefile	Mon Dec  3 20:34:01 2001
@@ -10,7 +10,7 @@
 SUB_DIRS     := block char net misc sound
 MOD_SUB_DIRS := $(SUB_DIRS)
 ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp \
-				macintosh video dio zorro fc4 usb telephony i2o
+				macintosh video dio zorro fc4 nubus usb telephony i2o
 
 ifdef CONFIG_DIO
 SUB_DIRS += dio
diff -urN linux-2.2.18-pre15/drivers/block/acsi.c linux/drivers/block/acsi.c
--- linux-2.2.18-pre15/drivers/block/acsi.c	Mon Dec  3 20:33:29 2001
+++ linux/drivers/block/acsi.c	Mon Dec  3 20:34:06 2001
@@ -252,17 +252,8 @@
 static char				*CurrentBuffer;
 
 
-#define SET_TIMER()							\
-	do {								\
-		del_timer( &acsi_timer );				\
-		acsi_timer.expires = jiffies + ACSI_TIMEOUT;		\
-		add_timer( &acsi_timer );				\
-	} while(0)
-
-#define CLEAR_TIMER()							\
-	do {								\
-		del_timer( &acsi_timer );				\
-	} while(0)
+#define SET_TIMER()	mod_timer(&acsi_timer, jiffies + ACSI_TIMEOUT)
+#define CLEAR_TIMER()	del_timer(&acsi_timer)
 
 static unsigned long	STramMask;
 #define STRAM_ADDR(a)	(((a) & STramMask) == 0)
@@ -424,8 +415,8 @@
 
 {
 	if (INT_LEVEL < 6) {
-		unsigned long maxjif;
-		for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); )
+		unsigned long maxjif = jiffies + timeout;
+		while (time_before(jiffies, maxjif))
 			if (!(mfp.par_dt_reg & 0x20)) return( 1 );
 	}
 	else {
@@ -441,8 +432,8 @@
 
 {
 	if (INT_LEVEL < 6) {
-		unsigned long maxjif;
-		for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); )
+		unsigned long maxjif = jiffies + timeout;
+		while (time_before(jiffies, maxjif))
 			if (mfp.par_dt_reg & 0x20) return( 1 );
 	}
 	else {
@@ -501,7 +492,7 @@
 #endif
 	
 	rwflag = rwflag ? 0x100 : 0;
-	paddr = VTOP( buffer );
+	paddr = virt_to_phys( buffer );
 
 	acsi_delay_end(COMMAND_DELAY);
 	DISABLE_IRQ();
@@ -609,7 +600,7 @@
 	if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
 	if (!acsi_wait_for_IRQ( 10 )) return( 0 );
 	acsi_getstatus();
-	dma_cache_maintenance( VTOP(buffer), 16, 0 );
+	dma_cache_maintenance( virt_to_phys(buffer), 16, 0 );
 	
 	return( 1 );
 }	
@@ -808,7 +799,7 @@
 		return;
 	}
 
-	dma_cache_maintenance( VTOP(CurrentBuffer), CurrentNSect*512, 0 );
+	dma_cache_maintenance( virt_to_phys(CurrentBuffer), CurrentNSect*512, 0 );
 	if (CurrentBuffer == acsi_buffer)
 		copy_from_acsibuffer();
 
@@ -1029,7 +1020,7 @@
 	 * consecutive buffers and thus can be done with a single command.
 	 */
 	buffer      = CURRENT->buffer;
-	pbuffer     = VTOP(buffer);
+	pbuffer     = virt_to_phys(buffer);
 	nsect       = CURRENT->current_nr_sectors;
 	CurrentNReq = 1;
 
@@ -1051,7 +1042,7 @@
 			unsigned long pendadr, pnewadr;
 			pendadr = pbuffer + nsect*512;
 			while( (bh = bh->b_reqnext) ) {
-				pnewadr = VTOP(bh->b_data);
+				pnewadr = virt_to_phys(bh->b_data);
 				if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break;
 				nsect += bh->b_size >> 9;
 				pendadr = pnewadr + bh->b_size;
@@ -1814,7 +1805,7 @@
 		unregister_blkdev( MAJOR_NR, "ad" );
 		return -ENOMEM;
 	}
-	phys_acsi_buffer = VTOP( acsi_buffer );
+	phys_acsi_buffer = virt_to_phys( acsi_buffer );
 	STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
 	
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
diff -urN linux-2.2.18-pre15/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c
--- linux-2.2.18-pre15/drivers/block/acsi_slm.c	Tue Dec 29 11:23:59 1998
+++ linux/drivers/block/acsi_slm.c	Mon Dec  3 20:34:06 2001
@@ -413,8 +413,8 @@
 
 	CMDSET_TARG_LUN( slmprint_cmd, sip->target, sip->lun );
 	cmd = slmprint_cmd;
-	paddr = VTOP( SLMBuffer );
-	dma_cache_maintenance( paddr, VTOP(BufferP)-paddr, 1 );
+	paddr = virt_to_phys( SLMBuffer );
+	dma_cache_maintenance( paddr, virt_to_phys(BufferP)-paddr, 1 );
 	DISABLE_IRQ();
 
 	/* Low on A1 */
@@ -466,7 +466,7 @@
 	addr = get_dma_addr();
 	stat = acsi_getstatus();
 	SLMError = (stat < 0)             ? SLMSTAT_ACSITO :
-		       (addr < VTOP(BufferP)) ? SLMSTAT_NOTALL :
+		       (addr < virt_to_phys(BufferP)) ? SLMSTAT_NOTALL :
 									    stat;
 
 	dma_wd.dma_mode_status = 0x80;
diff -urN linux-2.2.18-pre15/drivers/block/buddha.c linux/drivers/block/buddha.c
--- linux-2.2.18-pre15/drivers/block/buddha.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/buddha.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,168 @@
+/*
+ *  linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
+ *
+ *	Copyright (C) 1997 by Geert Uytterhoeven
+ *
+ *  This driver was written by based on the specifications in README.buddha.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ *  TODO:
+ *    - test it :-)
+ *    - tune the timings using the speed-register
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+
+#include "ide.h"
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+    /*
+     *  The Buddha has 2 IDE interfaces, the Catweasel has 3
+     */
+
+#define BUDDHA_NUM_HWIFS	2
+#define CATWEASEL_NUM_HWIFS	3
+
+
+    /*
+     *  Bases of the IDE interfaces (relative to the board address)
+     */
+
+#define BUDDHA_BASE1	0x800
+#define BUDDHA_BASE2	0xa00
+#define BUDDHA_BASE3	0xc00
+
+static const u_int buddha_bases[CATWEASEL_NUM_HWIFS] = {
+    BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#define BUDDHA_DATA	0x00
+#define BUDDHA_ERROR	0x06		/* see err-bits */
+#define BUDDHA_NSECTOR	0x0a		/* nr of sectors to read/write */
+#define BUDDHA_SECTOR	0x0e		/* starting sector */
+#define BUDDHA_LCYL	0x12		/* starting cylinder */
+#define BUDDHA_HCYL	0x16		/* high byte of starting cyl */
+#define BUDDHA_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
+#define BUDDHA_STATUS	0x1e		/* see status-bits */
+#define BUDDHA_CONTROL	0x11a
+
+static const u_int buddha_offsets[IDE_NR_PORTS] = {
+    BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+    BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL
+};
+
+
+    /*
+     *  Other registers
+     */
+
+#define BUDDHA_IRQ1	0xf00		/* MSB = 1, Harddisk is source of */
+#define BUDDHA_IRQ2	0xf40		/* interrupt */
+#define BUDDHA_IRQ3	0xf80
+
+static const int buddha_irqports[CATWEASEL_NUM_HWIFS] = {
+    BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
+};
+
+#define BUDDHA_IRQ_MR	0xfc0		/* master interrupt enable */
+
+
+    /*
+     *  Board information
+     */
+
+static u_long buddha_board = 0;
+static int buddha_num_hwifs = -1;
+
+
+    /*
+     *  Check and acknowledge the interrupt status
+     */
+
+static int buddha_ack_intr(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    return 1;
+}
+
+
+    /*
+     *  Any Buddha or Catweasel boards present?
+     */
+
+static int find_buddha(void)
+{
+    u_int key;
+    const struct ConfigDev *cd;
+
+    buddha_num_hwifs = 0;
+    if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, 0, 0)))
+	buddha_num_hwifs = BUDDHA_NUM_HWIFS;
+    else if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, 0,
+			       0)))
+	buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
+    if (key) {
+	cd = zorro_get_board(key);
+	buddha_board = (u_long)cd->cd_BoardAddr;
+	if (buddha_board) {
+	    buddha_board = ZTWO_VADDR(buddha_board);
+	    /* write to BUDDHA_IRQ_MR to enable the board IRQ */
+	    *(char *)(buddha_board+BUDDHA_IRQ_MR) = 0;
+	    zorro_config_board(key, 0);
+	}
+    }
+    return buddha_num_hwifs;
+}
+
+
+    /*
+     *  Probe for a Buddha or Catweasel IDE interface
+     *  We support only _one_ of them, no multiple boards!
+     */
+
+int buddha_probe_hwif(int index, ide_hwif_t *hwif)
+{
+    static int buddha_index[CATWEASEL_NUM_HWIFS] = { 0, };
+    int i;
+
+    if (buddha_num_hwifs < 0 && !find_buddha())
+	return 0;
+
+    for (i = 0; i < buddha_num_hwifs; i++) {
+	if (!buddha_index[i]) {
+	    printk("ide%d: %s IDE interface\n", index,
+		   buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" :
+		   					  "Catweasel");
+	    buddha_index[i] = index+1;
+	}
+	if (buddha_index[i] == index+1) {
+	    ide_setup_ports(hwif, (ide_ioreg_t)(buddha_board+buddha_bases[i]),
+	    		    buddha_offsets,
+			    (ide_ioreg_t)(buddha_board+buddha_irqports[i]),
+			    buddha_ack_intr);
+	    hwif->irq = IRQ_AMIGA_PORTS;
+	    return 1;
+	}
+    }
+    return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/block/falconide.c linux/drivers/block/falconide.c
--- linux-2.2.18-pre15/drivers/block/falconide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/falconide.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,72 @@
+/*
+ *  linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
+ *
+ *     Created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include "ide.h"
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+
+    /*
+     *  Base of the IDE interface
+     */
+
+#define ATA_HD_BASE	0xfff00000
+
+    /*
+     *  Offsets from the above base
+     */
+
+#define ATA_HD_DATA	0x00
+#define ATA_HD_ERROR	0x05		/* see err-bits */
+#define ATA_HD_NSECTOR	0x09		/* nr of sectors to read/write */
+#define ATA_HD_SECTOR	0x0d		/* starting sector */
+#define ATA_HD_LCYL	0x11		/* starting cylinder */
+#define ATA_HD_HCYL	0x15		/* high byte of starting cyl */
+#define ATA_HD_SELECT	0x19		/* 101dhhhh , d=drive, hhhh=head */
+#define ATA_HD_STATUS	0x1d		/* see status-bits */
+#define ATA_HD_CONTROL	0x39
+
+static const int falconide_offsets[IDE_NR_PORTS] = {
+    ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
+    ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL
+};
+
+
+    /*
+     *  Probe for a Falcon IDE interface
+     */
+
+int falconide_probe_hwif(int index, ide_hwif_t *hwif)
+{
+    static int falcon_index = 0;
+
+    if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE))
+	return 0;
+
+    if (!falcon_index) {
+	printk("ide%d: Falcon IDE interface\n", index);
+	falcon_index = index+1;
+    }
+    if (falcon_index == index+1) {
+	ide_setup_ports(hwif, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets, 0,
+			NULL);
+	hwif->irq = IRQ_MFP_IDE;
+	return 1;
+    }
+    return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/block/gayle.c linux/drivers/block/gayle.c
--- linux-2.2.18-pre15/drivers/block/gayle.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/gayle.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,169 @@
+/*
+ *  linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
+ *
+ *     Created 9 Jul 1997 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include "ide.h"
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/setup.h>
+
+
+    /*
+     *  Bases of the IDE interfaces
+     */
+
+#define GAYLE_BASE_4000	0xdd2020	/* A4000/A4000T */
+#define GAYLE_BASE_1200	0xda0000	/* A1200/A600 */
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#define GAYLE_DATA	0x00
+#define GAYLE_ERROR	0x06		/* see err-bits */
+#define GAYLE_NSECTOR	0x0a		/* nr of sectors to read/write */
+#define GAYLE_SECTOR	0x0e		/* starting sector */
+#define GAYLE_LCYL	0x12		/* starting cylinder */
+#define GAYLE_HCYL	0x16		/* high byte of starting cyl */
+#define GAYLE_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
+#define GAYLE_STATUS	0x1e		/* see status-bits */
+#define GAYLE_CONTROL	0x101a
+
+static u_int gayle_offsets[IDE_NR_PORTS] = {
+    GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
+    GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, GAYLE_CONTROL
+};
+
+
+    /*
+     *  These are at different offsets from the base
+     */
+
+#define GAYLE_IRQ_4000	0xdd3020	/* MSB = 1, Harddisk is source of */
+#define GAYLE_IRQ_1200	0xda9000	/* interrupt */
+
+
+    /*
+     *  Offset of the secondary port for IDE doublers
+     *  Note that GAYLE_CONTROL is NOT available then!
+     */
+
+#define GAYLE_NEXT_PORT	0x1000
+
+#ifndef CONFIG_BLK_DEV_IDEDOUBLER
+#define GAYLE_NUM_HWIFS		1
+#define GAYLE_NUM_PROBE_HWIFS	GAYLE_NUM_HWIFS
+#define GAYLE_HAS_CONTROL_REG	1
+#else /* CONFIG_BLK_DEV_IDEDOUBLER */
+#define GAYLE_NUM_HWIFS		2
+#define GAYLE_NUM_PROBE_HWIFS	(ide_doubler ? GAYLE_NUM_HWIFS : \
+					       GAYLE_NUM_HWIFS-1)
+#define GAYLE_HAS_CONTROL_REG	(!ide_doubler)
+int ide_doubler = 0;	/* support IDE doublers? */
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+
+    /*
+     *  Check and acknowledge the interrupt status
+     */
+
+static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    return 1;
+}
+
+static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+    outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]);
+    return 1;
+}
+
+
+    /*
+     *  Probe for a Gayle IDE interface (and optionally for an IDE doubler)
+     */
+
+int gayle_probe_hwif(int index, ide_hwif_t *hwif)
+{
+    static int gayle_index[GAYLE_NUM_HWIFS] = { 0, };
+    int a4000, i;
+
+    if (!MACH_IS_AMIGA)
+	return 0;
+
+    if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE))
+	return 0;
+
+    if (!GAYLE_HAS_CONTROL_REG)
+	gayle_offsets[IDE_CONTROL_OFFSET] = -1;
+
+    for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
+	if (!gayle_index[i]) {
+	    switch (i) {
+		case 0:
+		    printk("ide%d: Gayle IDE interface (A%d style)\n", index,
+			   a4000 ? 4000 : 1200);
+		    break;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+		case 1:
+		    printk("ide%d: IDE doubler\n", index);
+		    break;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+	    }
+	    gayle_index[i] = index+1;
+	}
+	if (gayle_index[i] == index+1) {
+	    ide_ioreg_t base, irqport;
+	    ide_ack_intr_t *ack_intr;
+	    if (a4000) {
+		base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000);
+		irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000);
+		ack_intr = gayle_ack_intr_a4000;
+	    } else {
+		base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200);
+		irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200);
+		ack_intr = gayle_ack_intr_a1200;
+	    }
+	    base += i*GAYLE_NEXT_PORT;
+	    ide_setup_ports(hwif, base, gayle_offsets, irqport, ack_intr);
+#if 1 /* TESTING */
+	    if (i == 1) {
+		volatile u_short *addr = (u_short *)base;
+		u_short data;
+		printk("+++ Probing for IDE doubler... ");
+		*addr = 0xffff;
+		data = *addr;
+		printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data);
+	    }
+#endif /* TESTING */
+	    hwif->irq = IRQ_AMIGA_PORTS;
+	    return 1;
+	}
+    }
+    return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/block/ide.c linux/drivers/block/ide.c
--- linux-2.2.18-pre15/drivers/block/ide.c	Mon Sep  4 10:39:16 2000
+++ linux/drivers/block/ide.c	Mon Dec  3 20:34:06 2001
@@ -1627,6 +1627,8 @@
 	ide_drive_t *drive;
 	ide_hwgroup_t *hwgroup;
 	unsigned int p, major, minor;
+	int old_blksize;
+	extern int *blksize_size[];
 	long flags;
 
 	if ((drive = get_info_ptr(i_rdev)) == NULL)
@@ -1634,6 +1636,7 @@
 	major = MAJOR(i_rdev);
 	minor = drive->select.b.unit << PARTN_BITS;
 	hwgroup = HWGROUP(drive);
+	old_blksize = blksize_size[major][minor];
 	spin_lock_irqsave(&io_request_lock, flags);
 	if (drive->busy || (drive->usage > 1)) {
 		spin_unlock_irqrestore(&io_request_lock, flags);
@@ -1651,7 +1654,10 @@
 			if (sb)
 				invalidate_inodes(sb);
 			invalidate_buffers (devp);
-			set_blocksize(devp, 1024);
+			if (!old_blksize)
+				set_blocksize(devp, 1024);
+			else
+				set_blocksize(devp, old_blksize);
 		}
 		drive->part[p].start_sect = 0;
 		drive->part[p].nr_sects   = 0;
diff -urN linux-2.2.18-pre15/drivers/block/macide.c linux/drivers/block/macide.c
--- linux-2.2.18-pre15/drivers/block/macide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/macide.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,122 @@
+/*
+ *  linux/drivers/block/macide.c -- Macintosh IDE Driver
+ *
+ *     Copyright (C) 1998 by Michael Schmitz
+ *
+ *  This driver was written based on information obtained from the MacOS IDE
+ *  driver binary by Mikael Forselius
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/delay.h>
+
+#include "ide.h"
+
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+
+    /*
+     *  Base of the IDE interface (see ATAManager ROM code)
+     */
+
+#define MAC_HD_BASE	0x50f1a000
+
+    /*
+     *  Offsets from the above base (scaling 4)
+     */
+
+#define MAC_HD_DATA	0x00
+#define MAC_HD_ERROR	0x04		/* see err-bits */
+#define MAC_HD_NSECTOR	0x08		/* nr of sectors to read/write */
+#define MAC_HD_SECTOR	0x0c		/* starting sector */
+#define MAC_HD_LCYL	0x10		/* starting cylinder */
+#define MAC_HD_HCYL	0x14		/* high byte of starting cyl */
+#define MAC_HD_SELECT	0x18		/* 101dhhhh , d=drive, hhhh=head */
+#define MAC_HD_STATUS	0x1c		/* see status-bits */
+#define MAC_HD_CONTROL	0x38		/* control/altstatus */
+
+static const int macide_offsets[IDE_NR_PORTS] = {
+    MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL,
+    MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL
+};
+
+	/*
+	 * Other registers
+	 */
+
+	/* 
+	 * IDE interrupt status register for both (?) hwifs on Quadra
+	 * Initial setting: 0xc
+	 * Guessing again:
+	 * Bit 0+1: some interrupt flags
+	 * Bit 2+3: some interrupt enable
+	 * Bit 4:   ??
+	 * Bit 5:   IDE interrupt flag (any hwif)
+	 * Bit 6:   maybe IDE interrupt enable (any hwif) ??
+	 * Bit 7:   Any interrupt condition
+	 *
+	 * Only relevant item: bit 5, to be checked by mac_ack_intr
+	 */
+
+#define MAC_HD_ISR	0x101
+
+static int mac_ack_intr(ide_hwif_t* hwif)
+{
+	unsigned char isr;
+	isr = readb(MAC_HD_BASE + MAC_HD_ISR);
+	if (isr & (1<<5)) {
+		writeb(isr & ~(1<<5), MAC_HD_BASE + MAC_HD_ISR);
+		return 1;
+	}
+
+	return 0;
+}
+
+    /*
+     *  Probe for a Macintosh IDE interface
+     */
+
+__initfunc(int macide_probe_hwif(int index, ide_hwif_t *hwif))
+{
+	static int mac_index = 0;
+
+	if (!MACH_IS_MAC || macintosh_config->ide_type == 0)
+		return 0;
+
+	if (!mac_index) {
+		if (macintosh_config->ide_type == MAC_IDE_QUADRA)
+			printk("ide%d: Macintosh Quadra IDE interface\n",
+			       index);
+		else
+			printk("ide%d: Macintosh Powerbook IDE interface\n",
+			       index);
+		mac_index = index+1;
+	}
+	if (mac_index == index+1) {
+		ide_setup_ports(hwif,
+				(ide_ioreg_t)MAC_HD_BASE,
+				macide_offsets, 
+				0, NULL);
+
+		if (macintosh_config->ide_type == MAC_IDE_QUADRA) {
+			hwif->irq = IRQ_NUBUS_F;
+		} else {
+			/* PowerBooks - Slot C is the Comm Slot on the
+			   Q630 */
+			hwif->irq = IRQ_NUBUS_C;
+		}
+		hwif->ack_intr = mac_ack_intr;
+
+		return 1;
+	}
+	return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/block/q40ide.c linux/drivers/block/q40ide.c
--- linux-2.2.18-pre15/drivers/block/q40ide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/q40ide.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,109 @@
+/*
+ *  linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver
+ *
+ *     original file created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ * RZ:
+ *  almost identical with pcide.c, maybe we can merge it later. 
+ *  Differences:
+ *       max 2 HWIFS for now
+ *       translate portaddresses to q40 native addresses (not yet...) instead rely on in/out[bw]
+ *         address translation
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include "ide.h"
+
+    /*
+     *  Bases of the IDE interfaces
+     */
+
+#define PCIDE_NUM_HWIFS	2
+
+#define PCIDE_BASE1	0x1f0
+#define PCIDE_BASE2	0x170
+#define PCIDE_BASE3	0x1e8
+#define PCIDE_BASE4	0x168
+#define PCIDE_BASE5	0x1e0
+#define PCIDE_BASE6	0x160
+
+static const q40ide_ioreg_t pcide_bases[PCIDE_NUM_HWIFS] = {
+    PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4  , PCIDE_BASE5,
+    PCIDE_BASE6 */
+};
+
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#undef HD_DATA
+#define HD_DATA  0x1f0
+
+#define PCIDE_REG(x)	((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1))
+
+static const int pcide_offsets[IDE_NR_PORTS] = {
+    PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR),
+    PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS),
+    PCIDE_REG(CMD)
+};
+
+int q40ide_default_irq(q40ide_ioreg_t base)
+{
+           switch (base) { 
+	            case 0x1f0: return 14;
+		    case 0x170: return 15;
+		    case 0x1e8: return 11;
+		    default:
+			return 0;
+	   }
+}
+
+void q40_ide_init_hwif_ports (q40ide_ioreg_t *p, q40ide_ioreg_t base, int *irq)
+{
+	q40ide_ioreg_t port = base;
+	int i = 8;
+
+	while (i--)
+		*p++ = port++;
+	*p++ = base + 0x206;
+	if (irq != NULL)
+		*irq = 0;
+}
+
+
+    /*
+     *  Probe for PC IDE interfaces
+     */
+
+int q40ide_probe_hwif(int index, ide_hwif_t *hwif)
+{
+    static int pcide_index[PCIDE_NUM_HWIFS] = { 0, };
+    int i;
+
+    if (!MACH_IS_Q40)
+      return 0;
+
+    for (i = 0; i < PCIDE_NUM_HWIFS; i++) {
+	if (!pcide_index[i]) {
+	  /*printk("ide%d: Q40 IDE interface\n", index);*/
+	    pcide_index[i] = index+1;
+	}
+	if (pcide_index[i] == index+1) {
+	    ide_setup_ports(hwif,(ide_ioreg_t) pcide_bases[i], pcide_offsets, 0, /*q40_ack_intr???*/ NULL);
+	    hwif->irq = ide_default_irq((ide_ioreg_t)pcide_bases[i]); /*q40_ide_irq[i];  */ /* 14 */
+	    return 1;
+	}
+    }
+    return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/block/swim_iop.c linux/drivers/block/swim_iop.c
--- linux-2.2.18-pre15/drivers/block/swim_iop.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/swim_iop.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,674 @@
+/*
+ * Driver for the SWIM (Super Woz Integrated Machine) IOP
+ * floppy controller on the Macintosh IIfx and Quadra 900/950
+ *
+ * Written by Joshua M. Thompson (funaho@jurai.org)
+ * based on the SWIM3 driver (c) 1996 by Paul Mackerras.
+ *
+ * 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.
+ *
+ * 1999-06-12 (jmt) - Initial implementation.
+ */
+
+/*
+ * -------------------
+ * Theory of Operation
+ * -------------------
+ *
+ * Since the SWIM IOP is message-driven we implement a simple request queue
+ * system.  One outstanding request may be queued at any given time (this is
+ * an IOP limitation); only when that request has completed can a new request
+ * be sent.
+ */
+
+/* This has to be defined before some of the #includes below */
+
+#define MAJOR_NR  FLOPPY_MAJOR
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/fd.h>
+#include <linux/blk.h>
+#include <linux/ioctl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mac_iop.h>
+#include <asm/swim_iop.h>
+
+#define DRIVER_VERSION "Version 0.1 (1999-06-12)"
+
+#define MAX_FLOPPIES	4
+
+#define IOCTL_MODE_BIT	8
+#define OPEN_WRITE_BIT	16
+
+enum swim_state {
+	idle,
+	available,
+	revalidating,
+	transferring,
+	ejecting
+};
+
+struct floppy_state {
+	enum swim_state state;
+	int	drive_num;	/* device number */
+	int	secpercyl;	/* disk geometry information */
+	int	secpertrack;
+	int	total_secs;
+	int	write_prot;	/* 1 if write-protected, 0 if not, -1 dunno */
+	int	ref_count;
+	struct timer_list timeout;
+	int	ejected;
+	struct wait_queue *wait;
+	int	wanted;
+	int	timeout_pending;
+};
+
+struct swim_iop_req {
+	int	sent;
+	int	complete;
+	__u8	command[32];
+	struct floppy_state *fs;
+	void	(*done)(struct swim_iop_req *);
+};
+
+static struct swim_iop_req *current_req;
+static int floppy_count;
+
+static struct floppy_state floppy_states[MAX_FLOPPIES];
+
+static int floppy_blocksizes[2] = {512,512};
+static int floppy_sizes[2] = {2880,2880};
+
+static char *drive_names[7] = {
+	"not installed",	/* DRV_NONE    */
+	"unknown (1)",		/* DRV_UNKNOWN */
+	"a 400K drive",		/* DRV_400K    */
+	"an 800K drive"		/* DRV_800K    */
+	"unknown (4)",		/* ????        */
+	"an FDHD",		/* DRV_FDHD    */
+	"unknown (6)",		/* ????        */
+	"an Apple HD20"		/* DRV_HD20    */
+};
+
+int swimiop_init(void);
+static void swimiop_init_request(struct swim_iop_req *);
+static int swimiop_send_request(struct swim_iop_req *);
+static void swimiop_receive(struct iop_msg *, struct pt_regs *);
+static void swimiop_status_update(int, struct swim_drvstatus *);
+static int swimiop_eject(struct floppy_state *fs);
+
+static ssize_t floppy_read(struct file *filp, char *buf,
+			   size_t count, loff_t *ppos);
+static ssize_t floppy_write(struct file *filp, const char *buf,
+			    size_t count, loff_t *ppos);
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long param);
+static int floppy_open(struct inode *inode, struct file *filp);
+static int floppy_release(struct inode *inode, struct file *filp);
+static int floppy_check_change(kdev_t dev);
+static int floppy_revalidate(kdev_t dev);
+static int grab_drive(struct floppy_state *fs, enum swim_state state,
+		      int interruptible);
+static void release_drive(struct floppy_state *fs);
+static void set_timeout(struct floppy_state *fs, int nticks,
+			void (*proc)(unsigned long));
+static void fd_request_timeout(unsigned long);
+static void do_fd_request(void);
+static void start_request(struct floppy_state *fs);
+
+static struct file_operations floppy_fops = {
+	NULL,			/* lseek */
+	floppy_read,		/* read */
+	floppy_write,		/* write */
+	NULL,			/* readdir */
+	NULL,			/* poll */
+	floppy_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	floppy_open,		/* open */
+	NULL,			/* flush */
+	floppy_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	floppy_check_change,	/* check_media_change */
+	floppy_revalidate,	/* revalidate */
+};
+
+/*
+ * SWIM IOP initialization
+ */
+
+int swimiop_init(void)
+{
+	volatile struct swim_iop_req req;
+	struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0];
+	struct swim_drvstatus *ds = &cmd->status;
+	struct floppy_state *fs;
+	int i;
+
+	current_req = NULL;
+	floppy_count = 0;
+
+	if (!iop_ism_present) return -ENODEV;
+
+	if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
+		printk(KERN_ERR "SWIM-IOP: Unable to get major %d for floppy\n",
+		       MAJOR_NR);
+		return -EBUSY;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	blksize_size[MAJOR_NR] = floppy_blocksizes;
+	blk_size[MAJOR_NR] = floppy_sizes;
+
+	printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n",
+		DRIVER_VERSION);
+
+	if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) {
+		printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n");
+		return -EBUSY;
+	}
+
+	printk(KERN_ERR "SWIM_IOP: probing for installed drives.\n");
+
+	for (i = 0 ; i < MAX_FLOPPIES ; i++) {
+		memset(&floppy_states[i], 0, sizeof(struct floppy_state));
+		fs = &floppy_states[floppy_count];
+
+		swimiop_init_request(&req);
+		cmd->code = CMD_STATUS;
+		cmd->drive_num = i + 1;
+		if (swimiop_send_request(&req) != 0) continue;
+		while (!req.complete);
+		if (cmd->error != 0) {
+			printk(KERN_ERR "SWIM-IOP: probe on drive %d returned error %d\n", i, (uint) cmd->error);
+			continue;
+		}
+		if (ds->installed != 0x01) continue;
+		printk("SWIM-IOP: drive %d is %s (%s, %s, %s, %s)\n", i,
+			drive_names[ds->info.type],
+			ds->info.external? "ext" : "int",
+			ds->info.scsi? "scsi" : "floppy",
+			ds->info.fixed? "fixed" : "removable",
+			ds->info.secondary? "secondary" : "primary");
+		swimiop_status_update(floppy_count, ds);
+		fs->state = idle;
+
+		init_timer(&fs->timeout);
+		floppy_count++;
+	}
+	printk("SWIM-IOP: detected %d installed drives.\n", floppy_count);
+
+	do_floppy = NULL;
+
+	return 0;
+}
+
+static void swimiop_init_request(struct swim_iop_req *req)
+{
+	req->sent = 0;
+	req->complete = 0;
+	req->done = NULL;
+}
+
+static int swimiop_send_request(struct swim_iop_req *req)
+{
+	unsigned long cpu_flags;
+	int err;
+
+	/* It's doubtful an interrupt routine would try to send */
+	/* a SWIM request, but I'd rather play it safe here.    */
+
+	save_flags(cpu_flags);
+	cli();
+
+	if (current_req != NULL) {
+		restore_flags(cpu_flags);
+		return -ENOMEM;
+	}
+
+	current_req = req;
+
+	/* Interrupts should be back on for iop_send_message() */
+
+	restore_flags(cpu_flags);
+
+	err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req,
+				sizeof(req->command), (__u8 *) &req->command[0],
+				swimiop_receive);
+
+	/* No race condition here; we own current_req at this point */
+
+	if (err) {
+		current_req = NULL;
+	} else {
+		req->sent = 1;
+	}
+	return err;
+}
+
+/*
+ * Receive a SWIM message from the IOP.
+ *
+ * This will be called in two cases:
+ *
+ * 1. A message has been successfully sent to the IOP.
+ * 2. An unsolicited message was received from the IOP.
+ */
+
+void swimiop_receive(struct iop_msg *msg, struct pt_regs *regs)
+{
+	struct swim_iop_req *req;
+	struct swimmsg_status *sm;
+	struct swim_drvstatus *ds;
+
+	req = current_req;
+
+	switch(msg->status) {
+		case IOP_MSGSTATUS_COMPLETE:
+			memcpy(&req->command[0], &msg->reply[0], sizeof(req->command));
+			req->complete = 1;
+			if (req->done) (*req->done)(req);
+			current_req = NULL;
+			break;
+		case IOP_MSGSTATUS_UNSOL:
+			sm = (struct swimmsg_status *) &msg->message[0];
+			ds = &sm->status;
+			swimiop_status_update(sm->drive_num, ds);
+			iop_complete_message(msg);
+			break;
+	}
+}
+
+static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds)
+{
+	struct floppy_state *fs = &floppy_states[drive_num];
+
+	fs->write_prot = (ds->write_prot == 0x80);
+	if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) {
+		fs->ejected = 1;
+	} else {
+		fs->ejected = 0;
+	}
+	switch(ds->info.type) {
+		case DRV_400K:
+			fs->secpercyl = 10;
+			fs->secpertrack = 10;
+			fs->total_secs = 800;
+			break;
+		case DRV_800K:
+			fs->secpercyl = 20;
+			fs->secpertrack = 10;
+			fs->total_secs = 1600;
+			break;
+		case DRV_FDHD:
+			fs->secpercyl = 36;
+			fs->secpertrack = 18;
+			fs->total_secs = 2880;
+			break;
+		default:
+			fs->secpercyl = 0;
+			fs->secpertrack = 0;
+			fs->total_secs = 0;
+			break;
+	}
+}
+
+static int swimiop_eject(struct floppy_state *fs)
+{
+	int err, n;
+	struct swim_iop_req req;
+	struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0];
+
+	err = grab_drive(fs, ejecting, 1);
+	if (err) return err;
+
+	swimiop_init_request(&req);
+	cmd->code = CMD_EJECT;
+	cmd->drive_num = fs->drive_num;
+	err = swimiop_send_request(&req);
+	if (err) {
+		release_drive(fs);
+		return err;
+	}
+	for (n = 2*HZ; n > 0; --n) {
+		if (req.complete) break;
+		if (signal_pending(current)) {
+			err = -EINTR;
+			break;
+		}
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+	release_drive(fs);
+	return cmd->error;
+}
+
+static ssize_t floppy_read(struct file *filp, char *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct floppy_state *fs;
+	int devnum = MINOR(inode->i_rdev);
+
+	if (devnum >= floppy_count)
+		return -ENODEV;
+		
+	fs = &floppy_states[devnum];
+	if (fs->ejected)
+		return -ENXIO;
+	return block_read(filp, buf, count, ppos);
+}
+
+static ssize_t floppy_write(struct file * filp, const char * buf,
+			    size_t count, loff_t *ppos)
+{
+	struct inode * inode = filp->f_dentry->d_inode;
+	struct floppy_state *fs;
+	int devnum = MINOR(inode->i_rdev);
+
+	if (devnum >= floppy_count)
+		return -ENODEV;
+	check_disk_change(inode->i_rdev);
+	fs = &floppy_states[devnum];
+	if (fs->ejected)
+		return -ENXIO;
+	if (fs->write_prot)
+		return -EROFS;
+	return block_write(filp, buf, count, ppos);
+}
+
+static struct floppy_struct floppy_type =
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL };	/*  7 1.44MB 3.5"   */
+
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long param)
+{
+	struct floppy_state *fs;
+	int err;
+	int devnum = MINOR(inode->i_rdev);
+
+	if (devnum >= floppy_count)
+		return -ENODEV;
+		
+	if (((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))) ||
+	    ((cmd & 0x80) && !suser()))
+		return -EPERM;
+
+	fs = &floppy_states[devnum];
+
+	switch (cmd) {
+	case FDEJECT:
+		if (fs->ref_count != 1)
+			return -EBUSY;
+		err = swimiop_eject(fs);
+		return err;
+	case FDGETPRM:
+	        err = copy_to_user((void *) param, (void *) &floppy_type,
+				   sizeof(struct floppy_struct));
+		return err;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int floppy_open(struct inode *inode, struct file *filp)
+{
+	struct floppy_state *fs;
+	int err;
+	int devnum = MINOR(inode->i_rdev);
+
+	if (devnum >= floppy_count)
+		return -ENODEV;
+	if (filp == 0)
+		return -EIO;
+		
+	fs = &floppy_states[devnum];
+	err = 0;
+	if (fs->ref_count == -1 || filp->f_flags & O_EXCL) return -EBUSY;
+
+	if (err == 0 && (filp->f_flags & O_NDELAY) == 0
+	    && (filp->f_mode & 3)) {
+		check_disk_change(inode->i_rdev);
+		if (fs->ejected)
+			err = -ENXIO;
+	}
+
+	if (err == 0 && (filp->f_mode & 2)) {
+		if (fs->write_prot)
+			err = -EROFS;
+	}
+
+	if (err) return err;
+
+	if (filp->f_flags & O_EXCL)
+		fs->ref_count = -1;
+	else
+		++fs->ref_count;
+
+	/* Allow ioctls if we have write-permissions even if read-only open */
+	if ((filp->f_mode & 2) || (permission(inode, 2) == 0))
+		filp->f_mode |= IOCTL_MODE_BIT;
+	if (filp->f_mode & 2)
+		filp->f_mode |= OPEN_WRITE_BIT;
+
+	return 0;
+}
+
+static int floppy_release(struct inode *inode, struct file *filp)
+{
+	struct floppy_state *fs;
+	int devnum = MINOR(inode->i_rdev);
+
+	if (devnum >= floppy_count)
+		return -ENODEV;
+
+	/*
+	 * If filp is NULL, we're being called from blkdev_release
+	 * or after a failed mount attempt.  In the former case the
+	 * device has already been sync'ed, and in the latter no
+	 * sync is required.  Otherwise, sync if filp is writable.
+	 */
+	if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+		block_fsync (filp, filp->f_dentry);
+
+	fs = &floppy_states[devnum];
+	if (fs->ref_count > 0) fs->ref_count--;
+	return 0;
+}
+
+static int floppy_check_change(kdev_t dev)
+{
+	struct floppy_state *fs;
+	int devnum = MINOR(dev);
+
+	if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
+		return 0;
+		
+	fs = &floppy_states[devnum];
+	return fs->ejected;
+}
+
+static int floppy_revalidate(kdev_t dev)
+{
+	struct floppy_state *fs;
+	int devnum = MINOR(dev);
+
+	if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
+		return 0;
+
+	fs = &floppy_states[devnum];
+
+	grab_drive(fs, revalidating, 0);
+	/* yadda, yadda */
+	release_drive(fs);
+
+	return 0;
+}
+
+static void floppy_off(unsigned int nr)
+{
+}
+
+static int grab_drive(struct floppy_state *fs, enum swim_state state,
+		      int interruptible)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (fs->state != idle) {
+		++fs->wanted;
+		while (fs->state != available) {
+			if (interruptible && signal_pending(current)) {
+				--fs->wanted;
+				restore_flags(flags);
+				return -EINTR;
+			}
+			interruptible_sleep_on(&fs->wait);
+		}
+		--fs->wanted;
+	}
+	fs->state = state;
+	restore_flags(flags);
+	return 0;
+}
+
+static void release_drive(struct floppy_state *fs)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	fs->state = idle;
+	start_request(fs);
+	restore_flags(flags);
+}
+
+static void set_timeout(struct floppy_state *fs, int nticks,
+			void (*proc)(unsigned long))
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	if (fs->timeout_pending)
+		del_timer(&fs->timeout);
+	fs->timeout.expires = jiffies + nticks;
+	fs->timeout.function = proc;
+	fs->timeout.data = (unsigned long) fs;
+	add_timer(&fs->timeout);
+	fs->timeout_pending = 1;
+	restore_flags(flags);
+}
+
+static void do_fd_request(void)
+{
+	int i;
+
+	for (i = 0 ; i < floppy_count ; i++) {
+		start_request(&floppy_states[i]);
+	}
+}
+
+static void fd_request_complete(struct swim_iop_req *req)
+{
+	struct floppy_state *fs = req->fs;
+	struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];
+
+	del_timer(&fs->timeout);
+	fs->timeout_pending = 0;
+	fs->state = idle;
+	if (cmd->error) {
+		printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error);
+		end_request(0);
+	} else {
+		CURRENT->sector += cmd->num_blocks;
+		CURRENT->current_nr_sectors -= cmd->num_blocks;
+		if (CURRENT->current_nr_sectors <= 0) {
+			end_request(1);
+			return;
+		}
+	}
+	start_request(fs);
+}
+
+static void fd_request_timeout(unsigned long data)
+{
+	struct floppy_state *fs = (struct floppy_state *) data;
+
+	fs->timeout_pending = 0;
+	end_request(0);
+	fs->state = idle;
+}
+
+static void start_request(struct floppy_state *fs)
+{
+	volatile struct swim_iop_req req;
+	struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];
+
+	if (fs->state == idle && fs->wanted) {
+		fs->state = available;
+		wake_up(&fs->wait);
+		return;
+	}
+	while (CURRENT && fs->state == idle) {
+		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+			panic(DEVICE_NAME ": request list destroyed");
+		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+			panic(DEVICE_NAME ": block not locked");
+#if 0
+		printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
+		       kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
+		       CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
+		printk("           rq_status=%d errors=%d current_nr_sectors=%ld\n",
+		       CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
+#endif
+
+		if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
+			end_request(0);
+			continue;
+		}
+		if (CURRENT->current_nr_sectors == 0) {
+			end_request(1);
+			continue;
+		}
+		if (fs->ejected) {
+			end_request(0);
+			continue;
+		}
+
+		swimiop_init_request(&req);
+		req.fs = fs;
+		req.done = fd_request_complete;
+
+		if (CURRENT->cmd == WRITE) {
+			if (fs->write_prot) {
+				end_request(0);
+				continue;
+			}
+			cmd->code = CMD_WRITE;
+		} else {
+			cmd->code = CMD_READ;
+
+		}
+		cmd->drive_num = fs->drive_num;
+		cmd->buffer = CURRENT->buffer;
+		cmd->first_block = CURRENT->sector;
+		cmd->num_blocks = CURRENT->current_nr_sectors;
+
+		if (swimiop_send_request(&req)) {
+			end_request(0);
+			continue;
+		}
+
+		set_timeout(fs, HZ*CURRENT->current_nr_sectors,
+				fd_request_timeout);
+
+		fs->state = transferring;
+	}
+}
diff -urN linux-2.2.18-pre15/drivers/block/z2ram.c linux/drivers/block/z2ram.c
--- linux-2.2.18-pre15/drivers/block/z2ram.c	Fri Dec 18 09:36:05 1998
+++ linux/drivers/block/z2ram.c	Mon Dec  3 20:34:06 2001
@@ -32,23 +32,18 @@
 #include <linux/malloc.h>
 #include <linux/blk.h>
 #include <linux/init.h>
-
-#if defined(MODULE)
 #include <linux/module.h>
-#endif
 
 #include <asm/setup.h>
 #include <asm/bitops.h>
 #include <asm/amigahw.h>
-#ifdef CONFIG_APUS
 #include <asm/pgtable.h>
 #include <asm/io.h>
-#endif
 #include <linux/zorro.h>
 
 
-extern int num_memory;
-extern struct mem_info memory[NUM_MEMINFO];
+extern int m68k_realnum_memory;
+extern struct mem_info m68k_memory[NUM_MEMINFO];
 
 #define TRUE                  (1)
 #define FALSE                 (0)
@@ -191,14 +186,14 @@
 		int index = device - Z2MINOR_MEMLIST1 + 1;
 		unsigned long size, paddr, vaddr;
 
-		if (index >= num_memory) {
+		if (index >= m68k_realnum_memory) {
 			printk( KERN_ERR DEVICE_NAME
 				": no such entry in z2ram_map\n" );
 			return -ENOMEM;
 		}
 
-		paddr = memory[index].addr;
-		size = memory[index].size & ~(Z2RAM_CHUNKSIZE-1);
+		paddr = m68k_memory[index].addr;
+		size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1);
 
 #ifdef __powerpc__
 		/* FIXME: ioremap doesn't build correct memory tables. */
@@ -212,8 +207,7 @@
 						   _PAGE_WRITETHRU);
 
 #else
-		vaddr = kernel_map (paddr, size, KERNELMAP_FULL_CACHING,
-				    NULL);
+		vaddr = (unsigned long)ioremap(paddr, size);
 #endif
 		z2ram_map = 
 			kmalloc((size/Z2RAM_CHUNKSIZE)*sizeof(z2ram_map[0]),
@@ -316,9 +310,7 @@
 	blk_size[ MAJOR_NR ] = z2_sizes;
     }
 
-#if defined(MODULE)
     MOD_INC_USE_COUNT;
-#endif
 
     return 0;
 }
@@ -331,9 +323,11 @@
 
     sync_dev( inode->i_rdev );
 
-#if defined(MODULE)
+    /*
+     * FIXME: unmap memory
+     */
+
     MOD_DEC_USE_COUNT;
-#endif
 
     return 0;
 }
diff -urN linux-2.2.18-pre15/drivers/char/amiga_ser.c linux/drivers/char/amiga_ser.c
--- linux-2.2.18-pre15/drivers/char/amiga_ser.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/amiga_ser.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,558 @@
+/*
+ * drivers/char/amiga_ser.c: Amiga built-in serial port driver.
+ *
+ * Copyright 1994 Roman Hodek, 1994 Hamish Macdonald
+ *       Based on the Atari MFP driver by Roman Hodek
+ *
+ * Modifications by Matthias Welwarsky <Matthias.Welwarsky@ppp.th-darmstadt.de>
+ * - fixed reentrancy problem in ser_tx_int()
+ * 
+ * 20/02/99 - Jesper Skov: Added mb() calls and KGDB support.
+ * 27/04/96 - Jes Soerensen: Upgraded for Linux-1.3.x.
+ * 02/09/96 - Jes Soerensen: Moved the {request,free}_irq call for
+ *            AMIGA_VERTB interrupts into the init/deinit funtions as
+ *            there is no reason to service the ser_vbl_int when the
+ *            serial port is not in use.
+ * 30/04/96 - Geert Uytterhoeven: Added incoming BREAK detection
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+/*
+ * This file implements the driver for the Amiga built-in serial port.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/interrupt.h>
+#include <linux/m68kserial.h>
+#include <linux/linkage.h>
+
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+
+/* some serial hardware definitions */
+#define SDR_OVRUN   (1<<15)
+#define SDR_RBF     (1<<14)
+#define SDR_TBE     (1<<13)
+#define SDR_TSRE    (1<<12)
+
+#define AC_SETCLR   (1<<15)
+#define AC_UARTBRK  (1<<11)
+
+#define SER_DTR     (1<<7)
+#define SER_RTS     (1<<6)
+#define SER_DCD     (1<<5)
+#define SER_CTS     (1<<4)
+#define SER_DSR     (1<<3)
+
+/***************************** Prototypes *****************************/
+
+static void ser_rx_int( int irq, void *data, struct pt_regs *fp);
+static void ser_tx_int( int irq, void *data, struct pt_regs *fp);
+static void ser_vbl_int( int irq, void *data, struct pt_regs *fp);
+static void ser_init( struct m68k_async_struct *info );
+static void ser_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void ser_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int  ser_check_custom_divisor (struct m68k_async_struct *info,
+				      int baud_base, int divisor);
+static void ser_change_speed( struct m68k_async_struct *info );
+static void ser_throttle( struct m68k_async_struct *info, int status );
+static void ser_set_break( struct m68k_async_struct *info, int break_flag );
+static void ser_get_serial_info( struct m68k_async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int ser_get_modem_info( struct m68k_async_struct *info );
+static int ser_set_modem_info( struct m68k_async_struct *info, int new_dtr,
+			      int new_rts );
+static void ser_stop_receive( struct m68k_async_struct *info );
+static int ser_trans_empty( struct m68k_async_struct *info );
+
+/************************* End of Prototypes **************************/
+
+
+
+/* SERIALSWITCH structure for the Amiga serial port
+ */
+
+static SERIALSWITCH amiga_ser_switch = {
+    ser_init, ser_deinit, ser_enab_tx_int,
+    ser_check_custom_divisor, ser_change_speed,
+    ser_throttle, ser_set_break,
+    ser_get_serial_info, ser_get_modem_info,
+    ser_set_modem_info, NULL, ser_stop_receive, ser_trans_empty, NULL
+};
+
+/* Standard speeds table */
+static int baud_table[19] = {
+    /* B0     */ 0,
+    /* B50    */ 50,
+    /* B75    */ 75,
+    /* B110   */ 110,
+    /* B134   */ 134,
+    /* B150   */ 150,
+    /* B200   */ 200,
+    /* B300   */ 300,
+    /* B600   */ 600,
+    /* B1200  */ 1200,
+    /* B1800  */ 1800,
+    /* B2400  */ 2400,
+    /* B4800  */ 4800,
+    /* B9600  */ 9600,
+    /* B19200 */ 19200,
+    /* B38400 */ 38400,
+    /* B57600 */ 57600,
+    /* B115200*/ 115200,
+    /* B230400*/ 230400
+};
+	
+static __inline__ void ser_DTRoff(void)
+{
+    ciab.pra |= SER_DTR; /* active low */
+}
+
+static __inline__ void ser_DTRon(void)
+{
+    ciab.pra &= ~SER_DTR; /* active low */
+}
+
+static __inline__ void ser_RTSoff(void)
+{
+    ciab.pra |= SER_RTS; /* active low */
+}
+
+static __inline__ void ser_RTSon(void)
+{
+    ciab.pra &= ~SER_RTS; /* active low */
+}
+
+static int line; /* the serial line assigned by register_serial() */
+/* use this value in isr's to get rid of the data pointer in the future */
+/* This variable holds the current state of the DCD/CTS bits */
+static unsigned char current_ctl_bits;
+
+static __inline__ void check_modem_status(struct m68k_async_struct *info)
+{
+	unsigned char bits;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	bits = ciab.pra & (SER_DCD | SER_CTS);
+
+	if (bits ^ current_ctl_bits) {
+		if ((bits ^ current_ctl_bits) & SER_DCD) {
+			rs_dcd_changed(info, !(bits & SER_DCD));
+		}
+
+		if ((bits ^ current_ctl_bits) & SER_CTS)
+			rs_check_cts(info, !(bits & SER_CTS));
+	}
+	current_ctl_bits = bits;
+}
+
+static struct m68k_async_struct *amiga_info;
+
+int amiga_serinit( void )
+{	
+	unsigned long flags;
+	struct serial_struct req;
+	struct m68k_async_struct *info;
+	
+	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
+		return -ENODEV;
+
+	req.line = -1; /* first free ttyS? device */
+	req.type = SER_AMIGA;
+	req.port = (int) &custom.serdatr; /* dummy value */
+	if ((line = m68k_register_serial( &req )) < 0) {
+		printk( "Cannot register built-in serial port: no free device\n" );
+		return -EBUSY;
+	}
+    info = &rs_table[line];
+	
+    save_flags (flags);
+    cli();
+
+    /* set ISRs, and then disable the rx interrupts */
+    request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", info);
+    request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0, "serial RX", info);
+
+    amiga_info = info;
+
+    /* turn off Rx and Tx interrupts */
+    custom.intena = IF_RBF | IF_TBE;
+
+    /* clear any pending interrupt */
+    custom.intreq = IF_RBF | IF_TBE;
+    restore_flags (flags);
+
+    /*
+     * set the appropriate directions for the modem control flags,
+     * and clear RTS and DTR
+     */
+    ciab.ddra |= (SER_DTR | SER_RTS);   /* outputs */
+    ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR);  /* inputs */
+    
+    info->sw   = &amiga_ser_switch;
+
+#ifdef CONFIG_KGDB
+    /* turn Rx interrupts on for GDB */
+    custom.intena = IF_SETCLR | IF_RBF;
+    ser_RTSon();
+#endif
+
+    return 0;
+}
+
+static void ser_rx_int(int irq, void *data, struct pt_regs *fp)
+{
+#ifdef CONFIG_KGDB
+      extern void breakpoint (void);
+      int ch;
+
+      ch = custom.serdatr;
+      mb();
+
+      custom.intreq = IF_RBF;
+
+      /* Break signal from GDB? */
+      if (0x03 == (ch & 0xff)) {
+	      /* FIXME: This way of doing a breakpoint sucks
+		 big time. I will fix it later. */
+	      breakpoint ();
+      }
+#else
+      struct m68k_async_struct *info = data;
+      int ch, err;
+
+      ch = custom.serdatr;
+      mb();
+
+      custom.intreq = IF_RBF;
+
+      if ((ch & 0x1ff) == 0)
+	  err = TTY_BREAK;
+      else if (ch & SDR_OVRUN)
+	  err = TTY_OVERRUN;
+      else
+	  err = 0;
+      rs_receive_char(info, ch & 0xff, err);
+#endif
+}
+
+static void ser_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+#ifndef CONFIG_KGDB
+      struct m68k_async_struct *info = data;
+      int ch;
+
+      if (custom.serdatr & SDR_TBE) {
+	   if ((ch = rs_get_tx_char(info)) >= 0)
+	        /* write next char */
+	        custom.serdat = ch | 0x100;
+	   if (ch == -1 || rs_no_more_tx( info ))
+	        /* disable tx interrupts */
+	        custom.intena = IF_TBE;
+      }
+#endif
+}
+
+
+static void ser_vbl_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+
+	check_modem_status(info);
+}
+
+
+static void ser_init( struct m68k_async_struct *info )
+{	
+#ifndef CONFIG_KGDB
+	request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0,
+		    "serial status", amiga_info);
+
+	/* enable both Rx and Tx interrupts */
+	custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+
+	/* turn on DTR and RTS */
+	ser_DTRon();
+	ser_RTSon();
+
+	/* remember current state of the DCD and CTS bits */
+	current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS);
+
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+
+static void ser_enab_tx_int (struct m68k_async_struct *info, int enab_flag)
+{
+#ifndef CONFIG_KGDB
+    if (enab_flag) {
+        unsigned long flags;
+	save_flags(flags);
+	cli();
+	custom.intena = IF_SETCLR | IF_TBE;
+	mb();
+	/* set a pending Tx Interrupt, transmitter should restart now */
+	custom.intreq = IF_SETCLR | IF_TBE;
+	mb();
+	restore_flags(flags);
+    } else {
+	/* disable Tx interrupt and remove any pending interrupts */
+        custom.intena = IF_TBE;
+	custom.intreq = IF_TBE;
+    }
+#endif
+}
+
+
+static void ser_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+#ifndef CONFIG_KGDB
+    /* disable Rx and Tx interrupt */
+    custom.intena = IF_RBF | IF_TBE;
+    mb();
+
+    /* wait for last byte to be completely shifted out */
+    while( !(custom.serdatr & SDR_TSRE) )
+	barrier();
+
+    /* drop RTS and DTR if required */
+    ser_RTSoff();
+    if (!leave_dtr)
+	ser_DTRoff();
+
+    free_irq(IRQ_AMIGA_VERTB, amiga_info);
+    MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static int ser_check_custom_divisor (struct m68k_async_struct *info,
+				     int baud_base, int divisor)
+{
+    /* allow any divisor */
+    return 0;
+}
+
+
+static void ser_change_speed( struct m68k_async_struct *info )
+{
+#ifndef CONFIG_KGDB
+    unsigned	cflag, baud, chsize, stopb, parity, aflags;
+    unsigned	div = 0;
+    int realbaud;
+
+    if (!info->tty || !info->tty->termios) return;
+
+    cflag  = info->tty->termios->c_cflag;
+    baud   = cflag & CBAUD;
+    chsize = cflag & CSIZE;
+    stopb  = cflag & CSTOPB;
+    parity = cflag & (PARENB | PARODD);
+    aflags = info->flags & ASYNC_SPD_MASK;
+
+    if (cflag & CRTSCTS)
+	info->flags |= ASYNC_CTS_FLOW;
+    else
+	info->flags &= ~ASYNC_CTS_FLOW;
+    if (cflag & CLOCAL)
+	info->flags &= ~ASYNC_CHECK_CD;
+    else
+	info->flags |= ASYNC_CHECK_CD;
+
+    if (baud & CBAUDEX)
+	baud = baud - (B57600 - B38400 - 1);
+
+
+    if (baud == 15) {
+	    switch (aflags) {
+	    case ASYNC_SPD_HI:
+		    baud += 1;
+		    break;
+	    case ASYNC_SPD_VHI:
+		    baud += 2;
+		    break;
+	    case ASYNC_SPD_SHI:
+		    baud += 3;
+		    break;
+	    case ASYNC_SPD_WARP:
+		    baud += 4;
+		    break;
+	    case ASYNC_SPD_CUST:
+		    div = info->custom_divisor;
+		    break;
+	    }
+    }
+    if (!div){
+	/* Maximum speed is 230400 :-) */
+	if (baud > 18) baud = 18;
+	realbaud = baud_table[baud];
+	if (realbaud)
+	    div = (amiga_colorclock+realbaud/2)/realbaud - 1;
+    }
+
+    if (div) {
+	    /* turn on DTR */
+	    ser_DTRon();
+    } else {
+	    /* speed == 0 -> drop DTR */
+	    ser_DTRoff();
+	    return;
+    }
+
+    /* setup the serial port period register */
+    custom.serper = div;
+#endif
+}
+
+
+static void ser_throttle( struct m68k_async_struct *info, int status )
+{
+#ifndef CONFIG_KGDB
+    if (status)
+	ser_RTSoff();
+    else
+	ser_RTSon();
+#endif
+}
+
+
+static void ser_set_break( struct m68k_async_struct *info, int break_flag )
+{
+#ifndef CONFIG_KGDB
+    if (break_flag)
+	custom.adkcon = AC_SETCLR | AC_UARTBRK;
+    else
+	custom.adkcon = AC_UARTBRK;
+#endif
+}
+
+
+static void ser_get_serial_info( struct m68k_async_struct *info,
+				 struct serial_struct *retinfo )
+{
+    retinfo->baud_base = amiga_colorclock;
+    retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int ser_get_modem_info( struct m68k_async_struct *info )
+{
+    unsigned int minfo = ciab.pra;
+
+    return(
+	   ((minfo & SER_DTR) ? 0 : TIOCM_DTR) |
+	   ((minfo & SER_RTS) ? 0 : TIOCM_RTS) |
+	   ((minfo & SER_DCD) ? 0 : TIOCM_CAR) |
+	   ((minfo & SER_CTS) ? 0 : TIOCM_CTS) |
+	   ((minfo & SER_DSR) ? 0 : TIOCM_DSR) |
+	   /* TICM_RNG */ 0
+	   );
+}
+
+
+static int ser_set_modem_info( struct m68k_async_struct *info,
+			       int new_dtr, int new_rts )
+{
+#ifndef CONFIG_KGDB
+    if (new_dtr == 0)
+	ser_DTRoff();
+    else if (new_dtr == 1)
+	ser_DTRon();
+
+    if (new_rts == 0)
+	ser_RTSoff();
+    else if (new_rts == 1)
+	ser_RTSon();
+
+    return 0;
+#endif
+}
+
+static void ser_stop_receive( struct m68k_async_struct *info )
+{
+#ifndef CONFIG_KGDB
+	/* disable receive interrupts */
+	custom.intena = IF_RBF;
+	/* clear any pending receive interrupt */
+	custom.intreq = IF_RBF;
+#endif
+}
+
+static int ser_trans_empty( struct m68k_async_struct *info )
+{
+       return (custom.serdatr & SDR_TSRE);
+}
+
+#ifdef CONFIG_KGDB
+int amiga_ser_out( unsigned char c )
+{
+	custom.serdat = c | 0x100;
+	mb();
+	while (!(custom.serdatr & 0x2000))
+		barrier();
+	return 1;
+}
+
+unsigned char amiga_ser_in( void )
+{
+	unsigned char c;
+
+	/* XXX: is that ok?? derived from amiga_ser.c... */
+	while( !(custom.intreqr & IF_RBF) )
+		barrier();
+	c = custom.serdatr;
+	/* clear the interrupt, so that another character can be read */
+	custom.intreq = IF_RBF;
+	return c;
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+	return amiga_serinit();
+}
+
+void cleanup_module(void)
+{
+	m68k_unregister_serial(line);
+	custom.intena = IF_RBF | IF_TBE; /* forbid interrupts */
+	custom.intreq = IF_RBF | IF_TBE; /* clear pending interrupts */
+	mb();
+	free_irq(IRQ_AMIGA_TBE, amiga_info);
+	free_irq(IRQ_AMIGA_RBF, amiga_info);
+}
+#endif
+
+
+/* ------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * End:
+ * ------------------------------------
+ */
diff -urN linux-2.2.18-pre15/drivers/char/amikeyb.c linux/drivers/char/amikeyb.c
--- linux-2.2.18-pre15/drivers/char/amikeyb.c	Mon Apr 26 13:25:54 1999
+++ linux/drivers/char/amikeyb.c	Mon Dec  3 20:34:06 2001
@@ -257,7 +257,7 @@
 	    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
 	    add_timer(&amikeyb_rep_timer);
 	}
-	handle_scancode(scancode, !break_flag);
+	handle_scancode(keycode, !break_flag);
     } else
 	switch (keycode) {
 	    case 0x78:
@@ -340,9 +340,4 @@
     k->rate  = key_repeat_rate  * 1000 / HZ;
     
     return( 0 );
-}
-
-/* for "kbd-reset" cmdline param */
-__initfunc(void amiga_kbd_reset_setup(char *str, int *ints))
-{
 }
diff -urN linux-2.2.18-pre15/drivers/char/atari_MFPser.c linux/drivers/char/atari_MFPser.c
--- linux-2.2.18-pre15/drivers/char/atari_MFPser.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_MFPser.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,946 @@
+/*
+ * drivers/char/atari_MFPser.c: Atari MFP serial ports implementation
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * Special thanks to Harun Scheutzow (developer of RSVE, RSFI, ST_ESCC and
+ * author of hsmoda-package) for the code to detect RSVE/RSFI.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+/* This file implements the MFP serial ports. These come in two
+ * flavors: with or without control lines (RTS, CTS, DTR, ...). They
+ * are distinguished by having two different types, MFP_CTRL and
+ * MFP_BARE, resp. Most of the low-level functions are the same for
+ * both, but some differ.
+ *
+ * Note that some assumptions are made about where to access the
+ * control lines. If the port type is MFP_CTRL, the input lines (CTS
+ * and DCD) are assumed to be in the MFP GPIP register, bits 1 and 2.
+ * The output lines (DTR and RTS) have to be in the Soundchip Port A,
+ * bits 3 and 4. This is the standard ST/TT assigment. If Atari will
+ * build a serial port in future, that uses other registers, you have
+ * to rewrite this code. But if the port type is MFP_BARE, no such
+ * assumptions are necessary. All registers needed are fixed by the
+ * MFP hardware. The only parameter is the MFP base address. This is
+ * used to implement Serial1 for the TT and the (not connected) MFP
+ * port of the Falcon.
+ *
+ * Juergen: changes based on Harun Scheutzows code
+ *   o added detection of RSVE, RSFI and possible PLL's
+ *   o set info->hub6 to identify speeder-hardware
+ *   o changed Tx output-level when transmitter is disabled
+ *   o no need for CONFIG_ATARI_MFPSER_EXT
+ *
+ *
+ *   o add delays for baudrate-setting to lock PLLs (RSFI, RSVE)
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/m68kserial.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/irq.h>
+
+#include "atari_MFPser.h"
+
+#define RSFI_DEBUG		/* undefine to get rid of "detect_MFP_speeder: clock[x] = y" */
+#define RSFI_PLL_LOCK_DELAY	/* use delay to allow PLL settling */
+
+/***************************** Prototypes *****************************/
+
+static void MFPser_init_port( struct m68k_async_struct *info, int type,
+			     int tt_flag );
+#ifdef MODULE
+static void MFPser_deinit_port( struct m68k_async_struct *info, int tt_flag );
+#endif
+static void MFPser_rx_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_rxerr_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_tx_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_dcd_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_cts_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_ri_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_init( struct m68k_async_struct *info );
+static void MFPser_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void MFPser_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int MFPser_check_custom_divisor (struct m68k_async_struct *info,
+					int baud_base, int divisor);
+static void MFPser_change_speed( struct m68k_async_struct *info );
+static void MFPctrl_throttle( struct m68k_async_struct *info, int status );
+static void MFPbare_throttle( struct m68k_async_struct *info, int status );
+static void MFPser_set_break( struct m68k_async_struct *info, int break_flag );
+static void MFPser_get_serial_info( struct m68k_async_struct *info, struct
+                                    serial_struct *retinfo );
+static unsigned int MFPctrl_get_modem_info( struct m68k_async_struct *info );
+static unsigned int MFPbare_get_modem_info( struct m68k_async_struct *info );
+static int MFPctrl_set_modem_info( struct m68k_async_struct *info, int new_dtr,
+                                   int new_rts );
+static int MFPbare_set_modem_info( struct m68k_async_struct *info, int new_dtr,
+                                   int new_rts );
+static void MFPser_stop_receive (struct m68k_async_struct *info);
+static int MFPser_trans_empty (struct m68k_async_struct *info);
+
+/************************* End of Prototypes **************************/
+
+
+/* SERIALSWITCH structures for MFP ports
+ * Most functions are common to MFP ports with or without control lines
+ */
+
+static SERIALSWITCH MFPctrl_switch = {
+	MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
+	MFPser_check_custom_divisor, MFPser_change_speed,
+	MFPctrl_throttle, MFPser_set_break,
+	MFPser_get_serial_info, MFPctrl_get_modem_info,
+	MFPctrl_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty,
+	NULL
+};
+
+static SERIALSWITCH MFPbare_switch = {
+	MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
+	MFPser_check_custom_divisor, MFPser_change_speed,
+	MFPbare_throttle, MFPser_set_break,
+	MFPser_get_serial_info, MFPbare_get_modem_info,
+	MFPbare_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty,
+	NULL
+};
+
+  /* MFP Timer Modes divided by 2 (this already done in the BAUD_BASE 
+   * The real 68901 prescaler factors are twice these values! 
+   * prescaler_factor[] = {4, 10, 16, 50, 64, 100, 200}
+   */
+int MFP_timer_modes[] = { 2, 5, 8, 25, 32, 50, 100 };
+ 
+  /* RSVE or RSSPEED will only recognize the 3 frequencies for 
+   * 110, 134, 150 Baud, if the prescaler is 4 and the counter value does 
+   * the rest. The divisors 350 and 256 can be built in multiple ways. 
+   * This driver tries to use the largest prescaler factor possible and 
+   * uses small counter values. TOS uses a prescaler factor of 4
+   * ==> MFP_timer_mode = 2 ==> Index 0. Then RSVE replaces the clock 
+   * correctly. Since the absolute frequencies don't have to be so accurate 
+   * but the the prescaler factor has to be 4 we have to make sure that the 
+   * divisors for the 'real' 110, 134, 150 baud can be built with a 
+   * prescaler factor > 4 and the divisors for the replaced 110, 134, 150
+   * baud can only be built with a prescaler factor of 4 and a larger 
+   * counter value. Accuracy is not so important here, since RSVE catches 
+   * the values by ignoring the lower 3 bits.
+   */
+
+   /* Added support for RSFI
+    * RSFI is a hardware-FIFO with the following features:
+    *  o 2048bit Rx-FIFO
+    *  o baudrates: 38400, 57600, 76800, 115200, 153600, 230400
+    *    baudrate-selection and FIFO-enable is done by setting
+    *    the effective baudrate to 50 .. 200. In contrast to the
+    *    RSVE the clockselection is independend from the prescale-
+    *    factor. Instead, the MFP must be in x1 clockmode.
+    *  o the FIFO is only enabled when speeds are above 19k2
+    *
+    *  Relationship between MFP-baudrate and RSFI-baudrate
+    *   MFP		mode	RSFI
+    *	 50	x1	 76.8K *)
+    *	 75	x1	153.6K *)
+    *	110	x1	 38.4K
+    *	134	x1	 57.6K
+    *	150	x1	115.2K
+    *	200	x1	230.4K
+    *
+    * *) Non-standard baudrates, not supported.
+    *   
+    * We keep the speeder-type in info->hub6. This flag is unused on
+    * non-Intel architectures.
+    *
+    */
+
+int MFP_baud_table[22] = { /* Divisors for standard speeds, RSVE & RSFI */
+  /* B0      */ 0,
+  /* B50     */ 768,
+  /* B75     */ 512,
+  /* B110    */ 350, /* really 109.71 bps */  /* MFP_mode 50 != 2 */
+  /* B134    */ 288, /* really 133.33 bps */  /* MFP_mode  8 != 2 */
+  /* B150    */ 256,                          /* MFP_mode 32 != 2 */
+  /* B200    */ 192,
+  /* B300    */ 128,
+  /* B600    */ 64,
+  /* B1200   */ 32,
+  /* B1800   */ 21,
+  /* B2400   */ 16,
+  /* B4800   */ 8,
+  /* B9600   */ 4,
+  /* B19200  */ 2,
+  /* B38400  */ 348,  /*  38.4K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* B57600  */ 286,  /*  57.6K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* B115200 */ 258,  /* 115.2K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* --------------- the following values are ignored in RSVE-mode */
+  /* B230400 */ 192,  /* 230.4K with RSFI */
+  /* B460800 */ 0,    /* illegal */
+};
+
+
+#define DEFAULT_STMFP_LINE	0	/* ttyS0 */
+#define DEFAULT_TTMFP_LINE	2	/* ttyS2 */
+
+static int stmfp_line = -1, ttmfp_line = -1;
+
+extern int atari_MFP_init_done;
+
+
+int atari_MFPser_init( void )
+
+{	struct serial_struct req;
+	int nr = 0;
+	extern char m68k_debug_device[];
+
+	if (!MACH_IS_ATARI)
+		return( -ENODEV );
+
+	if (ATARIHW_PRESENT(ST_MFP)) {
+		if (!strcmp( m68k_debug_device, "ser1" ))
+			printk(KERN_NOTICE "ST-MFP serial port used as debug device\n" );
+		else {
+			req.line = DEFAULT_STMFP_LINE;
+			req.type = SER_MFP_CTRL;
+			req.port = (int)&mfp;
+			if ((stmfp_line = register_serial( &req )) >= 0) {
+				MFPser_init_port( &rs_table[stmfp_line], req.type, 0 );
+				++nr;
+			}
+			else
+				printk(KERN_WARNING "Cannot allocate ttyS%d for ST-MFP\n", req.line );
+		}
+	}
+
+	if (ATARIHW_PRESENT(TT_MFP)) {
+		req.line = DEFAULT_TTMFP_LINE;
+		req.type = SER_MFP_BARE;
+		req.port = (int)&tt_mfp;
+		if ((ttmfp_line = register_serial( &req )) >= 0) {
+			MFPser_init_port( &rs_table[ttmfp_line], req.type, 1 );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for TT-MFP\n", req.line );
+	}
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+static void __inline__ set_timer_D(volatile struct MFP *thismfp,
+								   int baud, int prescale) {
+/* set timer d to given value, prescale 4
+ * allow PLL-settling (3 bit-times)
+ */
+
+    int count;
+
+	thismfp->tim_ct_cd &= 0xf8;		/* disable timer D */
+	thismfp->tim_dt_d = baud;		/* preset baudrate */
+	thismfp->tim_ct_cd |= prescale;	/* enable timer D, prescale N */
+
+	for( count = 6; count; --count ) {
+		thismfp->int_pn_b = ~0x10;
+		while( !(thismfp->int_pn_b & 0x10) )
+			;
+	}
+}
+
+static int detect_MFP_speeder(volatile struct MFP *currMFP) {
+/* try to autodetect RSVE, RSFI or similiar RS232 speeders
+ *
+ *  (c) Harun Scheutzow 
+ *	    developer of RSVE, RSFI, ST_ESCC and author of hsmoda-package
+ *
+ *  integrated by Juergen Orschiedt
+ *
+ * noise-free detection of Tx/Rx baudrate
+ *  - set MFP to loopback, syncmode, 8bpc, syncchar=0xff
+ *  - enable transmitter and measure time between syncdetect
+ * depending on the relationship between measured time and
+ * timer-d setting we can tell which (if any) speeder we have.
+ *
+ * returncodes:
+ *	0	something wrong (1200 too slow)
+ *	1	no speeder detected
+ *	2	RSVE detected
+ *	3	RSFI detected
+ *	4	PLL or fixed Baudrate (Hardware-hack)
+ */
+
+	int count, speeder;
+	unsigned int flags;
+	int imra, imrb;
+
+	/* prepare IRQ registers for measurement */
+
+	save_flags(flags);
+	cli();
+
+	imra = currMFP->int_mk_a;
+	imrb = currMFP->int_mk_b;
+
+	currMFP->int_mk_a = imra & 0xe1;	/* mask off all Rx/Tx ints */
+	currMFP->int_mk_b = imrb & 0xef;	/* disable timer d int in IMRB */
+	currMFP->int_en_b |= 0x10;		/* enable in IERB (to see pending ints) */
+	restore_flags(flags);
+
+	(void)currMFP->par_dt_reg;		/* consume some cycles */
+	(void)currMFP->par_dt_reg;
+
+	/* initialize MFP */
+	currMFP->rcv_stat = 0x00;		/* disable Rx */
+	currMFP->trn_stat = TSR_SOMODE_HIGH;	/* disable Tx, output-level high */
+	currMFP->sync_char= 0xff;		/* syncchar = 0xff */
+	currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_SYNC_MODE | UCR_CHSIZE_8);
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_LOOP); 
+
+	/* look at 1200 baud setting (== effective 19200 in syncmode) */
+	set_timer_D(currMFP, 0x10, 0x01);
+	save_flags(flags);
+	cli();
+
+	/* check for fixed speed / bad speed */
+	currMFP->rcv_stat = RSR_RX_ENAB;
+	count = -1;
+	do {
+	  continue_outer:
+		++count;
+		currMFP->int_pn_b = ~0x10;
+		do {
+			if (currMFP->int_pn_b & 0x10)
+				goto continue_outer;
+		} while( !(currMFP->rcv_stat & RSR_SYNC_SEARCH) && count <= 22 );
+	} while(0);
+	restore_flags(flags);
+
+	/* for RSxx or standard MFP we have 8 bittimes (count=16) */
+#ifdef	RSFI_DEBUG
+	printk(KERN_INFO "    detect_MFP_speeder: count[1200]=%d\n", count);
+#endif
+
+	if (count < 10)	
+		speeder = MFP_WITH_PLL;		/* less than 5 bittimes: primitive speeder */
+	else
+	if (count >22)
+		speeder = MFP_WITH_WEIRED_CLOCK;	/* something wrong - too slow! */
+	else {
+		/* 1200 baud is working, we neither have fixed clock nor simple PLL */ 
+		set_timer_D(currMFP, 0xaf, 0x01);
+		save_flags(flags);
+		cli();
+
+		/* check for RSxx or Standard MFP with 110 baud
+		 * timer D toggles each 290us
+		 *	      bps	sync char count
+		 * RSVE:    614400	 22.33		 13uS
+		 * RSFI:     38400	  1.39		208us
+		 * Standard:  1720	  0.06
+		 * 
+		 */
+		currMFP->int_pn_b = ~0x10;
+		/* syncronize to timer D */
+		while( !(currMFP->int_pn_b & 0x10) )
+			;
+		currMFP->int_pn_b = ~0x10;
+		count = -1;
+		do {
+		  continue_outer2:
+			currMFP->rcv_stat = 0;				/* disable Rx */
+			++count;
+			(void)currMFP->par_dt_reg;			/* delay */
+			currMFP->rcv_stat = RSR_RX_ENAB;	/* enable Rx */
+			nop();
+			do {
+				/* increment counter if sync char detected */
+				if (currMFP->rcv_stat & RSR_SYNC_SEARCH)
+					goto continue_outer2;
+			} while( !(currMFP->int_pn_b & 0x10) );
+		} while(0);
+
+#ifdef RSFI_DEBUG
+		printk(KERN_INFO "    detect_MFP_speeder: count[110]=%d\n", count);
+#endif
+
+		if (count < 1) speeder = MFP_STANDARD;	/* no speeder detected */
+		else
+			if (count > 4) speeder = MFP_WITH_RSVE;
+		else
+			speeder = MFP_WITH_RSFI;
+	}
+
+	restore_flags(flags);
+	currMFP->rcv_stat = 0x00;		/* disable Rx */
+	currMFP->trn_stat = TSR_SOMODE_HIGH;	/* disable Tx, output-level high */
+	currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_ASYNC_2 | UCR_CHSIZE_8);
+	currMFP->int_mk_a = imra;
+	currMFP->int_mk_b = imrb;
+	currMFP->int_en_b &= 0xef;
+	currMFP->int_pn_a = 0xe1;		/* mask off pending Rx/Tx */
+	currMFP->int_pn_b = 0xef;		/* mask off pending Timer D */
+	return speeder;
+
+}
+
+static void MFPser_init_port( struct m68k_async_struct *info, int type, int tt_flag)
+{
+	INIT_currMFP(info);
+	int speeder;
+	static char *speeder_name[]= {"", "", "PLL or fixed clock", "RSVE", "RSFI" };
+
+	/* look for possible speeders */
+	info->hub6 = speeder = detect_MFP_speeder((struct MFP *)currMFP);
+	if (speeder > MFP_STANDARD)
+	    printk(KERN_INFO "ttyS%d: Detected %s extension\n",
+		   info->line, speeder_name[speeder]);
+
+	
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 * all ints are choosen of type FAST, and they're really quite fast.
+	 * Furthermore, we have to account for the fact that these are three ints,
+	 * and one can interrupt another. So better protect them against one
+	 * another...
+	 */
+	request_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT,
+	            MFPser_tx_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP TX" : "ST-MFP TX", info);
+	request_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL,
+	            MFPser_rx_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP RX" : "ST-MFP RX", info);
+	request_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR,
+	            MFPser_rxerr_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP RX error" : "ST-MFP RX error", info);
+	/* Tx_err interrupt unused (it signals only that the Tx shift reg
+	 * is empty)
+	 */
+
+	if (type == SER_MFP_CTRL && !tt_flag) {
+		/* The DCD, CTS and RI ints are slow ints, because I
+		   see no races with the other ints */
+		request_irq(IRQ_MFP_DCD, MFPctrl_dcd_int, IRQ_TYPE_SLOW,
+		            "ST-MFP DCD", info);
+		request_irq(IRQ_MFP_CTS, MFPctrl_cts_int, IRQ_TYPE_SLOW,
+		            "ST-MFP CTS", info);
+		request_irq(IRQ_MFP_RI, MFPctrl_ri_int, IRQ_TYPE_SLOW,
+		            "ST-MFP RI", info);
+		/* clear RTS and DTR */
+		if (!atari_MFP_init_done)
+			/* clear RTS and DTR */
+			GIACCESS( GI_RTS | GI_DTR );
+	}
+
+	info->sw = (type == SER_MFP_CTRL ? &MFPctrl_switch : &MFPbare_switch);
+
+  	info->custom_divisor = 4;          /* 9600 Baud */
+  	info->baud_base = MFP_BAUD_BASE;
+
+	if (tt_flag || !atari_MFP_init_done) {
+		currMFP->rcv_stat  = 0;	/* disable Rx */
+		currMFP->trn_stat  = TSR_SOMODE_HIGH;	/* disable Tx */
+	}
+}
+
+
+#ifdef MODULE
+static void MFPser_deinit_port( struct m68k_async_struct *info, int tt_flag )
+{
+	free_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT, info);
+	free_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL, info);
+	free_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR, info);
+	if (info->type == SER_MFP_CTRL && !tt_flag) {
+		free_irq(IRQ_MFP_DCD, info );
+		free_irq(IRQ_MFP_CTS, info );
+		free_irq(IRQ_MFP_RI, info);
+	}
+}
+#endif
+
+
+static void MFPser_rx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	int		ch, stat, err;
+	INIT_currMFP(info);
+
+	stat = currMFP->rcv_stat;
+	ch   = currMFP->usart_dta;
+	/* Frame Errors don't cause a RxErr IRQ! */
+	err  = (stat & RSR_FRAME_ERR) ? TTY_FRAME : 0;
+
+	rs_receive_char (info, ch, err);
+}
+
+
+static void MFPser_rxerr_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	int		ch, stat, err;
+	INIT_currMFP(info);
+
+	stat = currMFP->rcv_stat;
+	ch   = currMFP->usart_dta; /* most probably junk data */
+
+	if (stat & RSR_PARITY_ERR)
+		err = TTY_PARITY;
+	else if (stat & RSR_OVERRUN_ERR)
+		err = TTY_OVERRUN;
+	else if (stat & RSR_BREAK_DETECT)
+		err = TTY_BREAK;
+	else if (stat & RSR_FRAME_ERR)	/* should not be needed */
+		err = TTY_FRAME;
+	else
+		err = 0;
+
+	rs_receive_char (info, ch, err);
+}
+
+
+static void MFPser_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	int ch;
+	INIT_currMFP(info);
+
+	if (currMFP->trn_stat & TSR_BUF_EMPTY) {
+		if ((ch = rs_get_tx_char( info )) >= 0)
+			currMFP->usart_dta = ch;
+		if (ch == -1 || rs_no_more_tx( info ))
+			/* disable tx interrupts */
+			currMFP->int_en_a &= ~0x04;
+	}
+}
+
+
+static void MFPctrl_dcd_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	INIT_currMFP(info);
+
+	/* Toggle active edge to get next change of DCD! */
+	currMFP->active_edge ^= GPIP_DCD;
+
+	rs_dcd_changed( info, !(currMFP->par_dt_reg & GPIP_DCD) );
+}
+
+
+static void MFPctrl_cts_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	INIT_currMFP(info);
+
+	/* Toggle active edge to get next change of CTS! */
+	currMFP->active_edge ^= GPIP_CTS;
+
+	rs_check_cts( info, !(currMFP->par_dt_reg & GPIP_CTS) );
+}
+
+
+static void MFPctrl_ri_int(int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	/* update input line counter */
+	info->icount.rng++;
+	wake_up_interruptible(&info->delta_msr_wait);
+}
+
+
+static void MFPser_init( struct m68k_async_struct *info )
+{
+	INIT_currMFP(info);
+
+	/* base value for UCR */
+	if (info->type != SER_MFP_CTRL || !atari_MFP_init_done)
+		currMFP->usart_ctr = (UCR_PARITY_OFF | UCR_ASYNC_1 |
+							  UCR_CHSIZE_8 | UCR_PREDIV);
+
+	/* enable Rx and clear any error conditions */
+	currMFP->rcv_stat = RSR_RX_ENAB;
+
+	/* enable Tx */
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH);
+
+	/* enable Rx, RxErr and Tx interrupts */
+	currMFP->int_en_a |= 0x1c;
+	currMFP->int_mk_a |= 0x1c;
+
+	if (info->type == SER_MFP_CTRL) {
+
+		int status;
+
+		/* set RTS and DTR (low-active!) */
+		GIACCESS( ~(GI_RTS | GI_DTR) );
+
+		/* Set active edge of CTS and DCD signals depending on their
+		 * current state.
+		 * If the line status changes between reading the status and
+		 * enabling the interrupt, this won't work :-( How could it be
+		 * done better??
+		 * ++andreas: do it better by looping until stable
+		 */
+		do {
+		    status = currMFP->par_dt_reg & GPIP_CTS;
+		    if (status)
+			currMFP->active_edge &= ~GPIP_CTS;
+		    else
+			currMFP->active_edge |= GPIP_CTS;
+		} while ((currMFP->par_dt_reg & GPIP_CTS) != status);
+
+		do {
+		    status = currMFP->par_dt_reg & GPIP_DCD;
+		    if (status)
+			currMFP->active_edge &= ~GPIP_DCD;
+		    else
+			currMFP->active_edge |= GPIP_DCD;
+		} while ((currMFP->par_dt_reg & GPIP_DCD) != status);
+
+		/* enable CTS and DCD interrupts */
+		currMFP->int_en_b |= 0x06;
+		currMFP->int_mk_b |= 0x06;
+	}
+	MOD_INC_USE_COUNT;
+}
+
+
+static void MFPser_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+	INIT_currMFP(info);
+	
+	/* disable Rx, RxErr and Tx interrupts */
+	currMFP->int_en_a &= ~0x1c;
+
+	if (info->type == SER_MFP_CTRL) {
+		/* disable CTS and DCD interrupts */
+		currMFP->int_en_b &= ~0x06;
+	}
+
+	/* disable Rx and Tx */
+	currMFP->rcv_stat = 0;
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
+
+	/* wait for last byte to be completely shifted out */
+	while( !(currMFP->trn_stat & TSR_LAST_BYTE_SENT) )
+		;
+
+	if (info->type == SER_MFP_CTRL) {
+		/* drop RTS and DTR if required */
+		MFPser_RTSoff();
+		if (!leave_dtr)
+			MFPser_DTRoff();
+	}
+
+	/* read Rx status and data to clean up */
+	(void)currMFP->rcv_stat;
+	(void)currMFP->usart_dta;
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void MFPser_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
+{
+	INIT_currMFP(info);
+
+	if (enab_flag) {
+		unsigned long flags;
+		currMFP->int_en_a |= 0x04;
+		save_flags(flags);
+		cli();
+		MFPser_tx_int (0, info, 0);
+		restore_flags(flags);
+	}
+	else
+		currMFP->int_en_a &= ~0x04;
+}
+
+
+static int MFPser_check_custom_divisor (struct m68k_async_struct *info,
+					int baud_base, int divisor)
+{
+	int		i;
+
+	if (baud_base != MFP_BAUD_BASE)
+		return -1;
+
+	/* divisor must be a multiple of 2 or 5 (because of timer modes) */
+	if (divisor == 0 || ((divisor & 1) && (divisor % 5) != 0)) return( -1 );
+
+	/* Determine what timer mode would be selected and look if the
+	 * timer value would be greater than 256
+	 */
+	for( i = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1; i >= 0; --i )
+		if (divisor % MFP_timer_modes[i] == 0) break;
+	if (i < 0) return( -1 ); /* no suitable timer mode found */
+
+	return (divisor / MFP_timer_modes[i] > 256);
+}
+
+
+static void MFPser_change_speed( struct m68k_async_struct *info )
+{
+	unsigned	cflag, baud, chsize, stopb, parity, aflags;
+	unsigned	div = 0, timer_val;
+	int			timer_mode;
+	unsigned long ipl;
+	INIT_currMFP(info);
+
+	if (!info->tty || !info->tty->termios) return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 4)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			if (info->hub6 > MFP_WITH_PLL)	/* speeder detected? */
+			    baud += 15;
+	}
+	if ((info->hub6 > MFP_WITH_PLL) && (baud == 15)) {
+		/* only for speeders... */
+		switch (aflags) {
+		case ASYNC_SPD_HI:
+			baud += 1;  /* 134 Baud, with RSVE =  57600 */
+			break;
+		case ASYNC_SPD_VHI:
+			baud += 2;  /* 150 Baud, with RSVE = 115200 */
+			break;
+		case ASYNC_SPD_SHI:
+			baud += 3;	/* with RSFI: 230400 Baud */
+			break;
+		case ASYNC_SPD_WARP:
+			baud += 4;
+			break;
+		case ASYNC_SPD_CUST:
+			div = info->custom_divisor;
+			break;
+		}
+	}
+	if (!div) {
+		/* max. tableentries depending on speeder type */
+		static int maxbaud[] = { 14, 14, 14, 17, 18};
+		if (baud > maxbaud[info->hub6])
+			baud = maxbaud[info->hub6];
+		div = MFP_baud_table[baud];
+	}
+
+	if (div) {
+		/* turn on DTR */
+		MFPser_DTRon ();
+	} else {
+		/* speed == 0 -> drop DTR */
+		MFPser_DTRoff();
+		return;
+	}
+
+	/* compute timer value and timer mode (garuateed to succeed, because
+	 * the divisor was checked before by check_custom_divisor(), if it
+	 * is used-supplied)
+	 */
+	for( timer_mode = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1;
+		 timer_mode >= 0; --timer_mode )
+		if (div % MFP_timer_modes[timer_mode] == 0) break;
+	timer_val = div / MFP_timer_modes[timer_mode];
+
+	save_flags (ipl);
+	cli();
+	/* disable Rx and Tx while changing parameters */
+	currMFP->rcv_stat = 0;
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
+
+#ifdef RSFI_PLL_LOCK_DELAY
+	currMFP->int_en_b |= 0x10;
+	set_timer_D(currMFP, timer_val, timer_mode+1);
+	currMFP->int_en_b &= 0xef;
+#else
+	/* stop timer D to set new timer value immediatly after re-enabling */
+	currMFP->tim_ct_cd &= ~0x07;
+	currMFP->tim_dt_d = timer_val;
+	currMFP->tim_ct_cd |= (timer_mode+1);
+#endif
+	{
+	    unsigned shadow_ctr;
+
+	    shadow_ctr = ((parity & PARENB) ?
+			 ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) :
+			 UCR_PARITY_OFF ) |
+			    ( chsize == CS5 ? UCR_CHSIZE_5 :
+			      chsize == CS6 ? UCR_CHSIZE_6 :
+			      chsize == CS7 ? UCR_CHSIZE_7 : UCR_CHSIZE_8 ) |
+			( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 );
+
+	    if ((baud < 15) || (info->hub6 != MFP_WITH_RSFI))
+		shadow_ctr |= UCR_PREDIV;
+	    currMFP->usart_ctr = shadow_ctr;
+	}
+
+	/* re-enable Rx and Tx */
+	currMFP->rcv_stat = RSR_RX_ENAB;
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH);
+	restore_flags (ipl);
+}
+
+static void MFPctrl_throttle( struct m68k_async_struct *info, int status )
+{
+	if (status)
+		MFPser_RTSoff();
+	else
+		MFPser_RTSon();
+}
+
+
+static void MFPbare_throttle( struct m68k_async_struct *info, int status )
+{
+	/* no-op */
+}
+
+
+static void MFPser_set_break( struct m68k_async_struct *info, int break_flag )
+{
+	INIT_currMFP(info);
+	
+	if (break_flag)
+		currMFP->trn_stat |= TSR_SEND_BREAK;
+	else
+		currMFP->trn_stat &= ~TSR_SEND_BREAK;
+}
+
+
+static void MFPser_get_serial_info( struct m68k_async_struct *info,
+				   struct serial_struct *retinfo )
+{
+	retinfo->baud_base = info->baud_base;
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int MFPctrl_get_modem_info( struct m68k_async_struct *info )
+{
+	unsigned	gpip, gi;
+	unsigned int ri;
+	unsigned long ipl;
+	INIT_currMFP(info);
+
+	save_flags (ipl);
+	cli();
+	gpip = currMFP->par_dt_reg;
+	gi   = GIACCESS( 0 );
+	restore_flags (ipl);
+
+	/* DSR is not connected on the Atari, assume it to be set;
+	 * RI is tested by the RI bitpos field of info, because the RI is
+	 * signalled at different ports on TT and Falcon
+	 * ++andreas: the signals are inverted!
+	 */
+	/* If there is a SCC but no TT_MFP then RI on the ST_MFP is
+	   used for SCC channel b */
+	if (ATARIHW_PRESENT (SCC) && !ATARIHW_PRESENT (TT_MFP))
+		ri = 0;
+	else if (currMFP == &mfp)
+		ri = gpip & GPIP_RI ? 0 : TIOCM_RNG;
+	else
+		ri = 0;
+	return (((gi   & GI_RTS  ) ? 0 : TIOCM_RTS) |
+		((gi   & GI_DTR  ) ? 0 : TIOCM_DTR) |
+		((gpip & GPIP_DCD) ? 0 : TIOCM_CAR) |
+		((gpip & GPIP_CTS) ? 0 : TIOCM_CTS) |
+		TIOCM_DSR | ri);
+}
+
+
+static unsigned int MFPbare_get_modem_info( struct m68k_async_struct *info )
+{
+	return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
+}
+
+
+static int MFPctrl_set_modem_info( struct m68k_async_struct *info,
+				  int new_dtr, int new_rts )
+{
+	if (new_dtr == 0)
+		MFPser_DTRoff();
+	else if (new_dtr == 1)
+		MFPser_DTRon();
+
+	if (new_rts == 0)
+		MFPser_RTSoff();
+	else if (new_rts == 1)
+		MFPser_RTSon();
+
+	return( 0 );
+}
+
+
+static int MFPbare_set_modem_info( struct m68k_async_struct *info,
+				  int new_dtr, int new_rts )
+{
+	/* no-op */
+
+	/* Is it right to return an error or should the attempt to change
+	 * DTR or RTS be silently ignored?
+	 */
+	return( -EINVAL );
+}
+
+static void MFPser_stop_receive (struct m68k_async_struct *info)
+{
+	INIT_currMFP(info);
+
+	/* disable rx and rxerr interrupt */
+	currMFP->int_en_a &= ~0x18;
+
+	/* disable receiver */
+	currMFP->rcv_stat = 0;
+	/* disable transmitter */
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
+}
+
+static int MFPser_trans_empty (struct m68k_async_struct *info)
+{
+	INIT_currMFP(info);
+	return (currMFP->trn_stat & TSR_LAST_BYTE_SENT) != 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return( atari_MFPser_init() );
+}
+
+void cleanup_module(void)
+{
+	if (stmfp_line >= 0) {
+		MFPser_deinit_port( &rs_table[stmfp_line], 0 );
+		unregister_serial( stmfp_line );
+	}
+	if (ttmfp_line >= 0) {
+		MFPser_deinit_port( &rs_table[ttmfp_line], 1 );
+		unregister_serial( ttmfp_line );
+	}
+}
+#endif
diff -urN linux-2.2.18-pre15/drivers/char/atari_MFPser.h linux/drivers/char/atari_MFPser.h
--- linux-2.2.18-pre15/drivers/char/atari_MFPser.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_MFPser.h	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,131 @@
+
+/*
+ * atari_MFPser.h: Definitions for MFP serial ports
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+#ifndef _ATARI_MFPSER_H
+#define _ATARI_MFPSER_H
+
+#define INIT_currMFP(info)	\
+	volatile struct MFP *currMFP = (volatile struct MFP *)(info->port)
+
+/* MFP input frequency, divided by 16 (USART prediv for async modes)
+ * and 2 because of each timer run _toggles_ the output, resulting in
+ * half the frequency, and 2 as greatest common divisor of all timer
+ * modes.
+ */
+#define	MFP_BAUD_BASE	(2457600/16/2/2)
+
+/* USART Control Register */
+
+#define	UCR_PARITY_MASK		0x06
+#define	UCR_PARITY_OFF		0x00
+#define	UCR_PARITY_ODD		0x04
+#define	UCR_PARITY_EVEN		0x06
+
+#define	UCR_MODE_MASK		0x18
+#define	UCR_SYNC_MODE		0x00
+#define	UCR_ASYNC_1			0x08
+#define	UCR_ASNYC_15		0x10
+#define	UCR_ASYNC_2			0x18
+
+#define	UCR_CHSIZE_MASK		0x60
+#define	UCR_CHSIZE_8		0x00
+#define	UCR_CHSIZE_7		0x20
+#define	UCR_CHSIZE_6		0x40
+#define	UCR_CHSIZE_5		0x60
+
+#define	UCR_PREDIV			0x80
+
+/* Receiver Status Register */
+
+#define	RSR_RX_ENAB			0x01
+#define	RSR_STRIP_SYNC		0x02
+#define	RSR_SYNC_IN_PRGR	0x04	/* sync mode only */
+#define	RSR_CHAR_IN_PRGR	0x04	/* async mode only */
+#define	RSR_SYNC_SEARCH		0x08	/* sync mode only */
+#define	RSR_BREAK_DETECT	0x08	/* async mode only */
+#define	RSR_FRAME_ERR		0x10
+#define	RSR_PARITY_ERR		0x20
+#define	RSR_OVERRUN_ERR		0x40
+#define	RSR_CHAR_AVAILABLE	0x80
+
+/* Transmitter Status Register */
+
+#define	TSR_TX_ENAB			0x01
+
+#define	TSR_SOMODE_MASK		0x06
+#define	TSR_SOMODE_OPEN		0x00
+#define	TSR_SOMODE_LOW		0x02
+#define	TSR_SOMODE_HIGH		0x04
+#define	TSR_SOMODE_LOOP		0x06
+
+#define	TSR_SEND_BREAK		0x08
+#define	TSR_LAST_BYTE_SENT	0x10
+#define	TSR_HALF_DUPLEX		0x20
+#define	TSR_UNDERRUN		0x40
+#define	TSR_BUF_EMPTY		0x80
+
+/* Control signals in the GPIP */
+
+#define	GPIP_DCD			0x02
+#define	GPIP_CTS			0x04
+#define	GPIP_RI				0x40
+
+/* MFP speeders */
+#define MFP_WITH_WEIRED_CLOCK	0x00
+#define MFP_STANDARD		0x01
+#define MFP_WITH_PLL		0x02
+#define MFP_WITH_RSVE		0x03
+#define MFP_WITH_RSFI		0x04
+
+
+/* Convenience routine to access RTS and DTR in the Soundchip: It sets
+ * the register to (oldvalue & mask) if mask is negative or (oldvalue
+ * | mask) if positive. It returns the old value. I guess the
+ * parameters will be constants most of the time, so gcc can throw
+ * away the if statement if it isn't needed.
+ */
+ 
+static __inline__ unsigned char GIACCESS( int mask )
+{
+	unsigned long   cpu_status;
+	unsigned char   old;
+
+	save_flags(cpu_status);
+	cli();
+
+	sound_ym.rd_data_reg_sel = 14;
+	old = sound_ym.rd_data_reg_sel;
+
+	if (mask) {
+		if (mask < 0)
+			sound_ym.wd_data = old & mask;
+		else
+			sound_ym.wd_data = old | mask;
+	}
+	
+	restore_flags(cpu_status);
+	return( old );
+}
+
+#define	GI_RTS				0x08
+#define	GI_DTR				0x10
+
+#define	MFPser_RTSon()		GIACCESS( ~GI_RTS )
+#define	MFPser_RTSoff()		GIACCESS( GI_RTS )
+#define	MFPser_DTRon()		GIACCESS( ~GI_DTR )
+#define	MFPser_DTRoff()		GIACCESS( GI_DTR )
+
+
+int atari_MFPser_init( void );
+
+#endif /* _ATARI_MFPSER_H */
diff -urN linux-2.2.18-pre15/drivers/char/atari_MIDI.c linux/drivers/char/atari_MIDI.c
--- linux-2.2.18-pre15/drivers/char/atari_MIDI.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_MIDI.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,567 @@
+/*
+ * drivers/char/atari_MIDI.c: Atari MIDI driver as serial port
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Modified for midi by Martin Schaller
+ */
+
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/m68kserial.h>
+#include <linux/init.h>
+
+#include <asm/traps.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atarikb.h>
+
+#include "atari_MIDI.h"
+
+
+/* Defines */
+
+/* #define _DEBUG_MIDI_ */
+
+#define DEFAULT_MIDI_LINE       5          /* ttyS5 */
+#define MIDI_BAUD_BASE          500000     /* 31250 */
+
+#define STOP_SHIFT 0
+#define PAR_SHIFT  4
+#define CS_SHIFT   8
+
+/* Prototypes */
+static void MIDI_init_port(struct m68k_async_struct* info, int type, int tt_flag);
+static void MIDI_int(void);      /* called from keyboard int handler */
+static void MIDI_init( struct m68k_async_struct * info );
+static void MIDI_deinit( struct m68k_async_struct * info, int leave_dtr );
+static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag );
+static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor);
+static void MIDI_change_speed( struct m68k_async_struct * info );
+static void MIDI_throttle( struct m68k_async_struct * info, int status );
+static void MIDI_set_break( struct m68k_async_struct * info, int break_flag );
+static unsigned int MIDI_get_modem_info( struct m68k_async_struct * info );
+static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo);
+static int MIDI_set_modem_info( struct m68k_async_struct* info, int new_dtr, int new_rts );
+static void MIDI_stop_receive(struct m68k_async_struct * info);
+static int MIDI_trans_empty(struct m68k_async_struct * info);
+
+
+struct m68k_async_struct*            midi_info;
+static unsigned char            mid_ctrl_shadow;   /* Bernd Harries, 960525 */
+static unsigned char            mid_stat_shadow;   /* Bernd Harries, 961207 */
+
+
+#ifdef _BHA_MIDI_CHAR_FMT
+static int MIDI_char_fmt[8] = {
+    (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT)            | (2 << STOP_SHIFT),
+    (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (2 << STOP_SHIFT),
+    (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT)            | (1 << STOP_SHIFT),
+    (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT),
+    (CS8 << CS_SHIFT) | (0 << PAR_SHIFT)                 | (2 << STOP_SHIFT),
+    (CS8 << CS_SHIFT) | (0 << PAR_SHIFT)                 | (1 << STOP_SHIFT),
+    (CS8 << CS_SHIFT) | (PARENB << PAR_SHIFT)            | (1 << STOP_SHIFT),
+    (CS8 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT),
+};
+#endif
+
+  /* index = P * 4 + L * 2 + S * 1 */
+  /* S = (2 == Stopbits)           */
+  /* L = (8 == Databits)           */
+  /* P = Parity {Odd, Even, None}  */
+
+static char ACIA_char_fmt[16] = {
+    ACIA_D7O1S,
+    ACIA_D7O2S,
+    ACIA_D8O1S,
+    -1, 
+    ACIA_D7E1S,
+    ACIA_D7E2S,
+    ACIA_D8E1S,
+    -1, 
+    -1, 
+    -1, 
+    ACIA_D8N1S,
+    ACIA_D8N2S,
+    -2
+};
+
+/* Divisors for standard speeds & RSVE   Bernd Harries 961127 */
+static int MIDI_baud_table[20] = {
+    /* B0      */ 0,    /* invalid */
+    /* B50     */ 0,
+    /* B75     */ 0,
+    /* B110    */ 0,
+    /* B134    */ 0,
+    /* B150    */ 0,
+    /* B200    */ 0,
+    /* B300    */ 0,
+    /* B600    */ 0,
+    /* B1200   */ 0,
+    /* B1800   */ 0,
+    /* B2400   */ 0,
+    /* B4800   */ 0,    /* invalid */
+    /* B9600   */ 64,   /* really 7812.5 bps */
+    /* B19200  */ 0,    /* invalid */
+    /* B38400  */ 16,   /* really 31250 bps */
+    /* B57600  */ 0,    /* invalid */
+    /* B115200 */ 0,    /* invalid */
+    /* B230400 */ 0,    /* invalid */
+    /* B460800 */ 1     /* really 500000 bps */
+};
+
+  /* The following 2 arrays must be congruent! */
+static int ACIA_prescaler_modes[] = { 1, 16, 64 };
+static char ACIA_baud_masks[] = { ACIA_DIV1, ACIA_DIV16, ACIA_DIV64 };
+
+  /* 
+   * SERIALSWITCH structures for MIDI port
+   */
+
+static SERIALSWITCH  MIDI_switch = {
+    MIDI_init,
+    MIDI_deinit,
+    MIDI_enab_tx_int,
+    MIDI_check_custom_divisor,
+    MIDI_change_speed,
+    MIDI_throttle,
+    MIDI_set_break,
+    MIDI_get_serial_info,
+    MIDI_get_modem_info,
+    MIDI_set_modem_info,
+    NULL,                   /* MIDI_ioctl, */
+    MIDI_stop_receive,
+    MIDI_trans_empty,
+    NULL                    /* MIDI_check_open */
+};
+
+
+__initfunc(int atari_MIDI_init( void ))
+{
+	extern char  m68k_debug_device[];
+  	static  struct serial_struct  req;
+	int  midi_line;
+	int  nr = 0;
+  
+#ifdef _DEBUG_MIDI_
+	printk(" atari_MIDI_init() \n");
+#endif
+
+	if (!strcmp( m68k_debug_device, "midi" ))
+    		printk(KERN_NOTICE "MIDI serial port used as debug device\n" );
+	else {
+		req.line = DEFAULT_MIDI_LINE;
+    		req.type = SER_MIDI;
+    		req.port = (int) &acia.mid_ctrl;
+
+    		midi_line = register_serial( &req );
+    		if (midi_line >= 0) {
+      			MIDI_init_port(&rs_table[midi_line], req.type, 0);
+      			++nr;
+    		}
+    		else {
+      			printk(KERN_WARNING "Cannot allocate ttyS%d for MIDI\n", req.line );
+    		}
+  	}
+  	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void MIDI_init_port(struct m68k_async_struct * info, int type, int tt_flag)
+{
+  	midi_info = info;    /* modulglobal !!!!! */
+
+  	info->sw = &MIDI_switch;
+  	info->custom_divisor = 16;          /* 31250 Baud */
+  	info->baud_base = MIDI_BAUD_BASE;
+  	info->sw = &MIDI_switch;
+
+
+  /* set ISRs, but don't enable interrupts yet (done in init());
+   * all ints are choosen of type FAST, and they're really quite fast.
+   * Furthermore, we have to account for the fact that these are three ints,
+   * and one can interrupt another. So better protect them against one
+   * another...
+   */
+  /*
+	request_irq(IRQ_MIDI_SEREMPT, MIDI_tx_int, IRQ_TYPE_FAST, "MIDI TX", info);
+	request_irq(IRQ_MIDI_RECFULL, MIDI_rx_int, IRQ_TYPE_FAST, "MIDI RX", info);
+	request_irq(IRQ_MIDI_RECERR,
+    			MIDI_rxerr_int,
+    			IRQ_TYPE_FAST,
+    			"MIDI RX error",
+    			info);
+   */
+ 
+  /* Tx_err interrupt unused (it signals only that the Tx shift reg
+   * is empty)
+   */
+  /* Leave RTS high for now if selected as a switch, so that it's still valid
+   * as long as the port isn't opened.
+   */
+  	mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S |
+			  ((atari_switches&ATARI_SWITCH_MIDI) ?
+			   ACIA_RHTID : ACIA_RLTID);
+  	acia.mid_ctrl = mid_ctrl_shadow;
+
+  	atari_MIDI_interrupt_hook = MIDI_int;
+}
+
+
+static void MIDI_int(void)    /* called from keyboard int handler */
+{
+	static int    err;  
+/*  	register int  stat; */
+	register int  ch;
+
+	mid_stat_shadow = acia.mid_ctrl;
+
+	/* if == Rx Data Reg Full -> Interrupt */
+	if (mid_stat_shadow & (ACIA_RDRF | ACIA_FE | ACIA_OVRN)) {
+		ch = acia.mid_data;
+		err = 0;
+		if(mid_stat_shadow & ACIA_FE)      err = TTY_FRAME;
+		if(mid_stat_shadow & ACIA_OVRN)    err = TTY_OVERRUN;
+		rs_receive_char(midi_info, ch, err);
+		/* printk("R"); */
+	}
+
+	if (acia.mid_ctrl & ACIA_TDRE) {   /* Tx Data Reg Empty  Transmit Interrupt */
+		ch = rs_get_tx_char( midi_info );
+
+		if (ch >= 0) {         /* 32 Bit */
+			acia.mid_data = ch;
+			/* printk("_%c", ch); */
+		}
+
+		if (ch == -1 || rs_no_more_tx( midi_info )) {
+			/* disable tx interrupts    %x01xxxxx ==> %x00xxxxx */
+			/* RTS Low, Tx IRQ Disabled */
+			mid_ctrl_shadow &= ~ACIA_RLTIE;
+			acia.mid_ctrl = mid_ctrl_shadow;
+			/* printk("T"); */
+		}
+	}
+}
+
+static void MIDI_init( struct m68k_async_struct * info )
+{  
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_init() \n");
+#endif
+	/* Baud = DIV16, 8N1, denable rx interrupts */
+	mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+	acia.mid_ctrl = mid_ctrl_shadow;
+
+	MOD_INC_USE_COUNT;
+}
+
+static void MIDI_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_deinit() \n");
+#endif
+
+	/* seems like the status register changes on read */
+#ifdef _MIDI_WAIT_FOR_TX_EMPTY_
+	while(!(mid_stat_shadow & ACIA_TDRE)) {  /* wait for TX empty */
+		;
+		/* printk("m"); */
+	}
+#endif
+
+	/* Baud = DIV16, 8N1, disable Rx and Tx interrupts */
+	mid_ctrl_shadow =  ACIA_DIV16 | ACIA_D8N1S;
+	acia.mid_ctrl = mid_ctrl_shadow;
+
+	/* read Rx status and data to clean up */
+	(void)acia.mid_ctrl;
+	(void)acia.mid_data;
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * ACIA Control Register can only be written! Read accesses Status Register!
+ * Shadowing may be nescessary here like for SCC
+ *
+ *  Bernd Harries, 960525  Tel.: +49-421-804309
+ *  harries@asrv01.atlas.de
+ *  Bernd_Harries@hb2.maus.de
+ */
+
+static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag )
+{
+	unsigned long  cpu_status;
+
+	if (enab_flag) {
+		register int  ch;
+
+		save_flags(cpu_status);
+		cli();
+		/* RTS Low, Tx IRQ Enabled */
+		mid_ctrl_shadow |= ACIA_RLTIE;
+		acia.mid_ctrl = mid_ctrl_shadow;
+		/* restarted the transmitter */
+		/* 
+		 * These extensions since 0.9.5 are only allowed, if the 
+		 * Tx Data Register is Empty!
+		 * In 1.2.13pl10 this did not work. So I added the if().
+		 *
+		 * Bernd Harries                         960530
+		 *   harries@atlas.de
+		 *   harries@asrv01.atlas.de
+		 *   Bernd_Harries@hb2.maus.de
+		 */
+		if (acia.mid_ctrl & ACIA_TDRE) {  /* If last Char since disabling is gone */
+			ch = rs_get_tx_char( midi_info );
+			if (ch >= 0) {
+				acia.mid_data = ch;
+				/* printk("=%c", ch); */
+			}
+
+			if (ch == -1 || rs_no_more_tx( midi_info )) {
+				/* disable tx interrupts */
+				/* RTS Low, Tx IRQ Disabled */
+				mid_ctrl_shadow &= ~ACIA_RLTIE;
+				acia.mid_ctrl = mid_ctrl_shadow;
+
+			}
+		}
+		restore_flags(cpu_status);
+	} else {
+		save_flags(cpu_status);
+		cli();
+		/* RTS Low, Tx IRQ Disabled */
+		mid_ctrl_shadow &= ~ACIA_RLTIE;
+		acia.mid_ctrl = mid_ctrl_shadow;
+		restore_flags(cpu_status);
+	}
+}
+
+
+static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor)
+{  
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_check_custom_divisor() \n");
+#endif
+
+	if (baud_base != MIDI_BAUD_BASE)  return -1;
+
+	/* divisor must be a multiple of 1, 16, 64  */
+
+	switch (divisor) {
+		case 1:
+		case 16:
+		case 64:  return(0);	
+	}
+
+	return(-1);
+}
+
+
+static void MIDI_change_speed( struct m68k_async_struct *info )
+{
+	unsigned long   cpu_status;
+	unsigned int    baud, stopb, parity, aflags;
+	unsigned int    div, cflag, chsize;
+	int             timer_mode;
+	int             index;
+
+	unsigned char   mid_ctrl_new;
+	unsigned char   baud_mask; 
+	unsigned char   chsize_mask; 
+	unsigned char   fmt_mask; 
+
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_change_speed() \n");
+#endif
+
+	div = 0;
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else 
+		info->flags &= ~ASYNC_CTS_FLOW;
+  	
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 4)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		switch (aflags) {
+			case ASYNC_SPD_HI:
+				baud = 16;  /* 134 Baud, with RSVE =  57600 */
+				break;
+			case ASYNC_SPD_VHI:
+				baud = 17;  /* 150 Baud, with RSVE = 115200 */
+				break;
+			case ASYNC_SPD_SHI:
+				baud = 18;
+				break;
+			case ASYNC_SPD_WARP:
+				baud = 19;
+				break;
+			case ASYNC_SPD_CUST:
+				div = info->custom_divisor;
+				break;
+		}
+	}
+
+	if (!div) {
+		/* Maximum MIDI speed is 500000  */
+		if (baud > 19)  baud = 19;
+		div = MIDI_baud_table[baud];
+	}
+
+	if (div) {
+		/* turn on DTR */
+		/* MIDI_DTRon(); */
+	} else {
+		/* speed == 0 -> drop DTR */
+		/* MIDI_DTRoff(); */
+		return;
+	}
+
+	mid_ctrl_new = 0;
+	for (timer_mode = 2; timer_mode >= 0; timer_mode--)
+		if (ACIA_prescaler_modes[timer_mode] == div)  
+			break;
+
+	baud_mask = ACIA_baud_masks[timer_mode];
+
+	chsize_mask = 0;
+	if (chsize == CS8)  chsize_mask = ACIA_D8N2S;
+
+	index = 0;
+	if (stopb)  index |= (1 << 0);
+	if (chsize == CS8) index |= (1 << 1);
+	if (parity == 0)
+		index |= (2 << 2);
+	else if (parity == PARENB)
+		index |= (1 << 2);
+	else {  /* if(parity == PARENB | PARODD) */
+		/* index |= (0 << 2); */
+	}
+
+	fmt_mask = ACIA_char_fmt[index];
+
+	/* Now we have all parameters and can go to set them: */
+	save_flags(cpu_status);
+	cli();
+
+	/* disable Rx and Tx while changing parameters 
+	 * stop timer D to set new timer value immediatly after re-enabling
+	 */
+	mid_ctrl_shadow &= ~ACIA_RESET;   /* MASK_OUT significant bits */
+	mid_ctrl_shadow |= baud_mask;
+	mid_ctrl_shadow &= ~ACIA_D8O1S;
+	mid_ctrl_shadow |= fmt_mask;
+
+	acia.mid_ctrl = mid_ctrl_shadow;    /* Write only */
+	restore_flags(cpu_status);
+
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_change_speed() done. \n");
+#endif
+}
+
+
+static void MIDI_throttle( struct m68k_async_struct * info, int status )
+{
+	if (status)  ;    /* MIDI_RTSoff(); */
+	else         ;    /* MIDI_RTSon();  */
+}
+
+
+static void MIDI_set_break( struct m68k_async_struct * info, int break_flag )
+{
+}
+
+
+static unsigned int MIDI_get_modem_info( struct m68k_async_struct *info )
+{
+	return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
+}
+
+
+static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo )
+{
+#ifdef _DEBUG_MIDI_
+	printk(" MIDI_get_serial_info() \n");
+#endif
+
+	retinfo->baud_base = info->baud_base;
+	retinfo->custom_divisor =  info->custom_divisor;
+}
+
+
+static int MIDI_set_modem_info( struct m68k_async_struct *info, int new_dtr, int new_rts )
+{
+	/* Is it right to return an error or should the attempt to change
+ 	 * DTR or RTS be silently ignored?
+ 	 */
+	return( -EINVAL );
+}
+
+
+static void MIDI_stop_receive (struct m68k_async_struct *info)
+{
+	unsigned long  cpu_status;
+
+	save_flags(cpu_status);
+	cli();
+
+	/* disable receive interrupts */
+	mid_ctrl_shadow &= ~ACIA_RIE;
+	acia.mid_ctrl = mid_ctrl_shadow;
+	restore_flags(cpu_status);
+}
+
+
+static int MIDI_trans_empty (struct m68k_async_struct *info)
+{
+	return (acia.mid_ctrl & ACIA_TDRE) != 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	return( atari_MIDI_init() );
+}
+
+void cleanup_module(void)
+{
+	atari_MIDI_interrupt_hook = NULL;
+	unregister_serial( midi_info->line );
+}
+#endif
+
diff -urN linux-2.2.18-pre15/drivers/char/atari_MIDI.h linux/drivers/char/atari_MIDI.h
--- linux-2.2.18-pre15/drivers/char/atari_MIDI.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_MIDI.h	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,32 @@
+/*
+ * atari_MIDI.h: Definitions for the MIDI serial port
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#ifndef _ATARI_MIDI_H
+#define _ATARI_MIDI_H
+
+int atari_MIDI_init( void );
+
+
+#ifdef _MIDI_HSK_LINES_
+
+#define	MIDI_RTSon()
+#define	MIDI_RTSoff()
+#define	MIDI_DTRon()
+#define	MIDI_DTRoff()
+
+else
+
+#define	MIDI_RTSon
+#define	MIDI_RTSoff
+#define	MIDI_DTRon
+#define	MIDI_DTRoff
+
+#endif
+
+#endif /* _ATARI_MIDI_H */
diff -urN linux-2.2.18-pre15/drivers/char/atari_SCC.README linux/drivers/char/atari_SCC.README
--- linux-2.2.18-pre15/drivers/char/atari_SCC.README	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_SCC.README	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,194 @@
+atari_SCC.README
+================
+
+ This is the README covering the new DMA features of the Atari SCC driver.  It
+works reasonably well for me, but you may disagree. If so, please contact:
+
+TeSche (Torsten Scherer)
+itschere@techfak.uni-bielefeld.de
+
+
+0) Intro
+
+ As a funny intro let me cite the AMD Am8530H technical manual: "Use of a DMA
+controller is suggested for any system that transmits above 500 kb/s". Harhar,
+good joke...
+
+
+1) Supported Hardware / Software
+
+ That's simple to answer: Currently only the TT is supported, and since all
+other Atari machines lack DMA support for the SCC this isn't likely to
+change. There have been reports about some Falcons with Afterburner card(?)
+claiming they have DMA, but they don't, believe me. I've strengthened the
+condition to try DMA to 'DMA available and TT-MFP available', so you should be
+on the safer side. However, for the severe cases it can be completely disabled
+by a config option.
+
+ With the same hardware reason all numbers or ratings you may occasionally read
+in this file are measured on a stock TT with monochrome monitor.
+
+ The SCC_DMA driver has been tested with speeds up to 115k2 baud. It may work
+with something higher, but I'm not sure about the 230k4 and 560k8 that have
+recently been added, meaning, I haven't tested it because I can't. In theory it
+should work with 230k4 baud with the default parameters (see below), but not
+with 560k8.
+
+
+2) Basic Principle of Operation
+
+ This driver uses DMA to support receiving of data. It does not use DMA to
+transmit data. Roman Hodek once philosophized about doing a little statistic
+comparing how much data was received and how much was transmitted to decide
+whether to use DMA for the next receive or the next transmit each time it
+becomes vacant. That's a) horribly complicated and b) discards the fact that we
+all have problems with receiver overruns, and not with transmit underruns. So
+only DMA receive is supported. And, hey, I said he philosophized, I didn't say
+he was thinking about it seriously. :)
+
+ There's another problem with general purpose communication: You don't know in
+advance how many data will arrive. Ok, in case of IP you do after you've read
+the header, but that's just a special case. In general this leads to that the
+driver doesn't know how many data is to be expected and thus can't set up the
+DMA for exactly that amount of data. What it does is set up DMA on a buffer
+that it thinks it large enough and let a timer routine flush this buffer
+asyncronously.
+
+ This timer routine must stop DMA, read restbytes and restart DMA on another
+buffer very, very quickly. It doesn't even flush the buffers on its own, but
+uses a bottom half handler for this. Since this one can be delayed for many
+reasons it's not enough to have just two buffers to switch between (like TTY
+flip buffers), but instead you need more.
+
+ These buffers are directly flushed to the TTY's receive function, may that be
+a PPP line discipline or whatever. The whole concept of flip buffers is totally
+circumvented, as these buffers are very likely to run over when switching VTYs,
+in fact I'd say you can regard this as a law. :(
+
+ A nice little side effect of DMA support is that you save a lot of
+cycles. When the whole stuff of delivering data (avoiding the byte-oriented
+flip buffer interface) and waking up listening programs has only to be done
+once for every 1k of data that saves you an awful lot of computing power. You
+can easily see that if you compare DMA supported download with uploads, which
+still operate the normal way.
+
+ If you should see a message like:
+
+Sep 25 22:27:26 zaphod kernel: SCC-A: 1 overrun, 0 parity, 0 frame errors
+
+ that means there have been errors in the serial link. Since 99% of all
+possible errors (given a correct serial parameter setting) are likely to be
+overrun errors and the main task of DMA support is exactly to get rid of them I
+chosed to print these information unconditionally each time an error
+occurs. You shouldn't really see a lot of them, in theory: None. If you see
+*some* be happy that they're less than with ordinary serial ports. If you don't
+think they're less, complain.
+
+ If you should see a message like:
+
+Sep 25 22:26:08 zaphod kernel: SCC-A: fatal buffer overflow, data lost!
+
+ that means that execution of the bottom half flusher was delayed so long that
+the timer routine didn't find a free buffer. This can only be caused by
+exhaustive VTY switching I guess, all other things that could happen are
+unlikely to block your system for so long. However, if it happens there's not
+very much to do besides increasing the number of buffers (see below).
+
+ If you should see a message like:
+
+Sep 26 10:39:32 zaphod kernel: SCC-A: DMA-INT occured, data lost!
+
+ that means that the timer routine itself has been delayed so long that the DMA
+counter went to zero already. There's not very much to do about this, because
+since both the timer and the end-of-dma INT are IPL6 the latter one is probably
+also delayed and thus data is already lost. The only thing you can do is
+increase the size of the buffers.
+
+ So there are three basic parameters: The number of buffers to use, their size
+and the flushing frequency.
+
+
+3) Operating Parameters
+
+ Somewhere at the beginning of atari_SCC.c you'll find a short section that
+defines some parameters of the DMA support, namely the number and size of
+buffers. The flushing rate is encoded a bit more implicitly more downwards. My
+defaults for a TT are 8 buffers of 2k and a flushing rate of 12Hz.
+
+ 12Hz flushing rate at a receive speed of 115k2 baud leads to 960 chars per
+buffer. Choosing 1k looked to be a good idea, but extensive SCSI activity
+ruined that one. Since the buffer size must be a power of two I've chosen 2k.
+If the bottom half flusher finds a buffer which is more than 95% full it prints
+a warning. If you should see lots of these messages it can only mean you're
+running something very much higher than 115k2 baud, in which case you should
+increase the buffer size yet more (to 4k). If you're not running at higher
+rates you've got the problem that the flusher gets extremely delayed. You can
+still increase the buffer size, but you should better find out what's delaying
+the flusher and fix that.
+
+ The number of buffers is also experimental: I've seen that on a TT with
+monochrome screen a VTY switching can 'use' 3 or 4 buffers, sometimes 5. So
+with 8 buffers you should be on the safer side. You can activate the diagnostic
+printk in SCC_flush() if you want to check that out. You may choose any number
+of buffers, no restriction on this one.
+
+ I might add code to dynamically select these parameters depending upon the
+current speed later, but for the moment this should work nicely.
+
+
+4) Debugging
+
+ It is possible to define DEBUG_DMA at the beginning of atari_SCC.c. In that
+case the driver will print lots of information about what it is doing with or
+getting from the DMA. It then uses a flushing frequency of 0.48Hz to allow you
+to follow the output with your eyes, but this also means that with a buffer
+size of 1k you shouldn't use more than 19k2 baud or else you'll get problems.
+
+
+5) Performance
+
+ I've written a small program which opens a tty in raw mode and dumps all data
+that arrives into a file while printing how fast data arrived. Using this
+program I can receive data at 115k2 baud without a single error, even while
+switching VTYs or SCSI disc accesses. This costs me almost no cycle. :)
+
+ When being logged in via the SCC and downloading data at 115k2 baud with rz I
+get an average transfer rate of 9800 cps over 10 megs, including 12 errors
+(whilst heaviest disc activity). No disc activity yields 10600 cps and 0
+errors. This costs me nearly 40% of my cycles, probably because that version of
+rz isn't very clever.
+
+ On a 115k2 baud PPP link I get an average paket loss of less than 2%, yielding
+at worst 7k/s.  When dialing into our university network with a 28.8 modem
+running 115k2 baud I get less than 1% paket loss, yielding at worst 2.5k/s
+(best case so far: 3.2k/s). In either case you can verify that ftp download
+only costs you some 5% of your cycles, whereas an upload costs you almost any
+cycle you've got.
+
+
+6) Problems
+
+ There're a couple of words to say about why I still get paket loss:
+
+ The paket loss is caused by 'bad fcs' fields, as you can see when you enable
+'kdebug' in pppd. I've tried dumping the bad pakets in the PPP layer, and it
+showed me that the normal data stream was interrupted by some trash data being
+inserted. I've also tried dumping the data at SCC level, and the trash was
+present there too.
+
+ Numerous experiments have shown that these errors occur when the SCC *sends*
+data while receiving, which is perfectly consistent with the fact that I don't
+get a single error with my own just-dump-it program. Anything you do which
+forces data to be send increases probability of these errors. In case of a PPP
+link running ftp chose a big MTU/MRU rate, as this reduces the amount of
+acknowledge packets to be send back and you can see that the error rate drops.
+
+ Since I couldn't find anything in the tx_int function which might cause this I
+think it's a hardware bug. It wouldn't be the first one my TT has got... :(
+
+ BUT: Whereever these problems may come from, they're *far* less disturbing
+than the SCC driver without DMA support, so I think this state is legal for the
+moment. :)
+
+04.Dec.1966,
+TeSche
diff -urN linux-2.2.18-pre15/drivers/char/atari_SCC.c linux/drivers/char/atari_SCC.c
--- linux-2.2.18-pre15/drivers/char/atari_SCC.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_SCC.c	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,2624 @@
+/*
+ * drivers/char/atari_SCC.c: Atari SCC serial ports implementation
+ *
+ * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * Some parts were taken from the (incomplete) SCC driver by Lars Brinkhoff
+ * <f93labr@dd.chalmers.se>
+ *
+ * Adapted to 1.2 by Andreas Schwab
+ *
+ * Adapted to support MVME147, MVME162 and BVME6000 by Richard Hirst
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Description/Notes:
+ *
+ *  - This driver currently handles the asynchronous modes of the SCC ports
+ *    only. Synchronous operation or packet modes aren't implemented yet.
+ *
+ *  - Since there are many variations how the SCC can be integrated, the
+ *    driver offers the possibility to provide the frequencies attached to the
+ *    various clock inputs via an ioctl (along with an externally calculated
+ *    baud table).
+ *
+ *  - I haven't spent much time for optimizations yet...
+ *
+ *  - Channel A is accessible via two different devices: ttyS3 and ttyS4. The
+ *    former is the RS232 "Serial2" port, the latter the RS422 "LAN" port.
+ *    Only one of these devices can be open at one time.
+ *
+ *  - ++TeSche 12/96: DMA support for channel A, see atari_SCC.README for details
+ *    send comments/problems to: itschere@techfak.uni-bielefeld.de
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/m68kserial.h>
+#include <linux/tqueue.h>
+#include <linux/malloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+#ifdef CONFIG_MVME147_SCC
+#include <asm/mvme147hw.h>
+#endif
+#ifdef CONFIG_MVME162_SCC
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef CONFIG_BVME6000_SCC
+#include <asm/bvme6000hw.h>
+#endif
+#ifdef CONFIG_ATARI
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#endif
+#include <asm/irq.h>
+#include <asm/atari_SCCserial.h>
+
+#include "atari_SCC.h"
+
+
+#if defined CONFIG_ATARI_SCC || defined CONFIG_ATARI_SCC_MODULE
+#define ENABLE_ATARI_SCC
+#endif
+
+#define	DEBUG_INT	0x01
+#define	DEBUG_INIT	0x02
+#define	DEBUG_THROTTLE	0x04
+#define	DEBUG_INFO	0x08
+#define	DEBUG_SPEED	0x10
+#define	DEBUG_OPEN	0x20
+#define	DEBUG_OVERRUNS	0x40
+/* warning: DEBUG_DMA will lead to a driver which is not able to operate at
+ * more than 19200 bps without causing overruns!
+ */
+#define DEBUG_DMA	0x80
+
+#define	DEBUG_ALL	0xffffffff
+#define	DEBUG_NONE	0
+
+#define	DEBUG	DEBUG_NONE
+
+#define CHANNEL_A	0
+#define CHANNEL_B	1
+
+
+/* Shadows for all SCC write registers */
+static unsigned char SCC_shadow[2][16];
+
+/* Location to access for SCC register access delay */
+static volatile unsigned char *scc_del;
+
+/* To keep track of STATUS_REG state for detection of Ext/Status int source */
+static unsigned char SCC_last_status_reg[2];
+
+/* This array tells the clocks connected to RTxC or TRxC, resp., (2nd
+ * index) for each channel (1st index).
+ *
+ * This table is initialzed for the TT. If we run on another machine,
+ * the values are changed by the initialization function.
+ */
+
+static unsigned SCC_clocks[2][2] = {
+	  /* RTxC */			/* TRxC */
+	{ SCC_BAUD_BASE_PCLK4,	SCC_BAUD_BASE_NONE },	/* Channel A */
+	{ SCC_BAUD_BASE_TIMC,	SCC_BAUD_BASE_BCLK }	/* Channel B */
+};
+
+/* The SCC's master clock (as variable, in case someone has unusual
+ * hardware)
+ */
+
+static unsigned SCC_PCLK = SCC_BAUD_BASE_PCLK;
+
+
+/* BRG values for the standard speeds and the various clock sources */
+
+typedef struct {
+	unsigned	clksrc;		/* clock source to use or -1 for not possible */
+	unsigned	div;		/* divisor: 1, 2 and 4 correspond to
+					 * direct 1:16, 1:32 and 1:64 modes,
+					 * divisors >= 4 yield a BRG value of
+					 * div/2-2 (in 1:16 mode)
+					 */
+} BAUD_ENTRY;
+
+/* A pointer for each channel to the current baud table */
+static BAUD_ENTRY *SCC_baud_table[2];
+
+/* Baud table format:
+ *
+ * Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or
+ * CLK_PCLK) and a divisor. The following rules apply to the divisor:
+ *
+ *   - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
+ *               the BRG)
+ *
+ *   - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
+ *
+ *   - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
+ *
+ */
+
+#ifdef ENABLE_ATARI_SCC
+/* This table is used if RTxC = 3.672 MHz. This is the case for TT's
+ * channel A and for both channels on the Mega STE/Falcon. (TRxC is unused)
+ */
+
+static BAUD_ENTRY bdtab_norm[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 4590 },
+	/* B75     */ { CLK_RTxC, 3060 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 1530 },
+	/* B200    */ { CLK_PCLK, 2516 },
+	/* B300    */ { CLK_PCLK, 1678 },
+	/* B600    */ { CLK_PCLK, 838 },
+	/* B1200   */ { CLK_PCLK, 420 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_PCLK, 210 },
+	/* B4800   */ { CLK_RTxC, 48 },
+	/* B9600   */ { CLK_RTxC, 24 },
+	/* B19200  */ { CLK_RTxC, 12 },
+	/* B38400  */ { CLK_RTxC, 6 },   /* #15 spd_extra */
+	/* B57600  */ { CLK_RTxC, 4 },   /* #16 spd_hi */
+	/* B115200 */ { CLK_RTxC, 2 },   /* #17 spd_vhi */
+	/* B230400 */ { CLK_RTxC, 1 },   /* #18 spd_shi */
+	/* B460800 */ { 0, 0 }           /* #19 spd_warp: Impossible */
+};
+
+/* This is a special table for the TT channel B with 307.2 kHz at RTxC
+ * and 2.4576 MHz at TRxC
+ */
+static BAUD_ENTRY bdtab_TTChB[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 384 },
+	/* B75     */ { CLK_RTxC, 256 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 128 },
+	/* B200    */ { CLK_RTxC, 96 },
+	/* B300    */ { CLK_RTxC, 64 },
+	/* B600    */ { CLK_RTxC, 32 },
+	/* B1200   */ { CLK_RTxC, 16 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_RTxC, 8 },
+	/* B4800   */ { CLK_RTxC, 4 },
+	/* B9600   */ { CLK_RTxC, 2 },
+	/* B19200  */ { CLK_RTxC, 1 },
+	/* B38400  */ { CLK_TRxC, 4 },
+	/* B57600  */ { CLK_TRxC, 2 }, /* 57600 is not possible, use 76800 instead */
+	/* B115200 */ { CLK_TRxC, 1 }, /* 115200 is not possible, use 153600 instead */
+	/* B230400 */ { 0, 0 },        /* #18 spd_shi: Impossible  */
+	/* B460800 */ { 0, 0 }         /* #19 spd_warp: Impossible */
+};
+#endif
+
+#ifdef CONFIG_MVME147_SCC
+/* This table is used if RTxC = pCLK = 5 MHz. This is the case for MVME147
+ */
+static BAUD_ENTRY bdtab_m147[19] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_PCLK, 6250 },
+	/* B75     */ { CLK_PCLK, 4166 },
+	/* B110    */ { CLK_PCLK, 2814 },
+	/* B134    */ { CLK_PCLK, 2322 },
+	/* B150    */ { CLK_PCLK, 2084 },
+	/* B200    */ { CLK_PCLK, 1562 },
+	/* B300    */ { CLK_PCLK, 1040 },
+	/* B600    */ { CLK_PCLK, 520 },
+	/* B1200   */ { CLK_PCLK, 260 },
+	/* B1800   */ { CLK_PCLK, 194 },
+	/* B2400   */ { CLK_PCLK, 130 },
+	/* B4800   */ { CLK_PCLK, 64 },
+	/* B9600   */ { CLK_PCLK, 32 },
+	/* B19200  */ { CLK_PCLK, 16 },
+	/* B38400  */ { CLK_PCLK, 8 },
+	/* B57600  */ { CLK_PCLK, 4 },
+	/* B115200 */ { CLK_PCLK, 2 },
+	/* B230400 */ { CLK_PCLK, 1 },   /* #18 spd_shi */
+};
+#endif
+
+#ifdef CONFIG_MVME162_SCC
+/* This table is used if RTxC = pCLK = 10 MHz. This is the case for MVME162
+ */
+static BAUD_ENTRY bdtab_mvme[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_PCLK, 12500 },
+	/* B75     */ { CLK_PCLK, 8332 },
+	/* B110    */ { CLK_PCLK, 5682 },
+	/* B134    */ { CLK_PCLK, 4646 },
+	/* B150    */ { CLK_PCLK, 4166 },
+	/* B200    */ { CLK_PCLK, 3124 },
+	/* B300    */ { CLK_PCLK, 2082 },
+	/* B600    */ { CLK_PCLK, 1042 },
+	/* B1200   */ { CLK_PCLK, 520 },
+	/* B1800   */ { CLK_PCLK, 390 },
+	/* B2400   */ { CLK_PCLK, 260 },
+	/* B4800   */ { CLK_PCLK, 130 },
+	/* B9600   */ { CLK_PCLK, 64 },
+	/* B19200  */ { CLK_PCLK, 32 },
+	/* B38400  */ { CLK_PCLK, 16 },
+	/* B57600  */ { CLK_PCLK, 8 },
+	/* B115200 */ { CLK_PCLK, 4 },
+	/* B230400 */ { CLK_PCLK, 2 },   /* #18 spd_shi */
+	/* B460800 */ { CLK_PCLK, 1 }    /* #19 spd_warp */
+};
+#endif
+
+#ifdef CONFIG_BVME6000_SCC
+
+/* This table is used if RTxC = 7.3728 MHz. This is the case for BVMs
+ */
+static BAUD_ENTRY bdtab_bvme[18] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 9216 },
+	/* B75     */ { CLK_RTxC, 6144 },
+	/* B110    */ { CLK_RTxC, 4188 },
+	/* B134    */ { CLK_RTxC, 3424 },
+	/* B150    */ { CLK_RTxC, 3072 },
+	/* B200    */ { CLK_RTxC, 2304 },
+	/* B300    */ { CLK_RTxC, 1536 },
+	/* B600    */ { CLK_RTxC, 768 },
+	/* B1200   */ { CLK_RTxC, 384 },
+	/* B1800   */ { CLK_RTxC, 256 },
+	/* B2400   */ { CLK_RTxC, 192 },
+	/* B4800   */ { CLK_RTxC, 96 },
+	/* B9600   */ { CLK_RTxC, 48 },
+	/* B19200  */ { CLK_RTxC, 24 },
+	/* B38400  */ { CLK_RTxC, 12 },
+	/* B57600  */ { CLK_RTxC, 8 },
+	/* B115200 */ { CLK_RTxC, 4 }
+};
+#endif
+
+
+/* User settable tables */
+static BAUD_ENTRY bdtab_usr[2][20];
+
+
+/*
+   Here are the values to compute the tables above. For each base
+   clock, the BRG values for the common bps rates are listed. The
+   divisor is (BRG+2)*2. For each clock, the 1:16 and 1:32 are also
+   usable (and the BRG isn't used). 1:64 is the same as BRG with
+   k==0. If more than clock source was possible for a bps rate I've
+   choosen the one with the smallest error.
+
+   For 307.2 kHz == base 19200:
+     50    bps -> 190
+     75    bps -> 126
+     110   bps -> 85 (really 110.34 bps, error 0.31 %)
+     134   bps -> 70 (really 133.33 bps, error 0.49 %)
+     150   bps -> 62
+     200   bps -> 46
+     300   bps -> 30
+     600   bps -> 14
+     1200  bps -> 6
+     1800  bps -> 3 (really 1920 bps, error 6.7 %)
+     2400  bps -> 2
+     4800  bps -> 0
+
+   For 2.4576 MHz == base 153600:
+     50    bps -> 1534
+     75    bps -> 1022
+     110   bps -> 696 (really 110.03 bps, error 0.027 %)
+     134   bps -> 571 (really 134.03 bps, error 0.022 %)
+     150   bps -> 510
+     200   bps -> 382
+     300   bps -> 254
+     600   bps -> 126
+     1200  bps -> 62
+     1800  bps -> 41 (really 1786.1 bps, error 0.77 %)
+     2400  bps -> 30
+     4800  bps -> 14
+     9600  bps -> 6
+     19200 bps -> 2
+     38400 bps -> 0
+
+   For 3.672 MHz == base 229500:
+     50    bps -> 2293
+     75    bps -> 1528
+     110   bps -> 1041
+     134   bps -> 854
+     150   bps -> 763
+     200   bps -> 572
+     300   bps -> 381
+     600   bps -> 189
+     1200  bps -> 94
+     1800  bps -> 62
+     2400  bps -> 46
+     4800  bps -> 22
+     9600  bps -> 10
+     19200 bps -> 4
+     38400 bps -> 1
+     57600 bps -> 0
+
+   For 8.053976 MHz == base 503374:
+	  0    	 bps -> 0
+	  50     bps -> 5032
+	  75     bps -> 3354
+	  110    bps -> 2286
+	  134    bps -> 1876
+	  150	 bps -> 1676
+	  200	 bps -> 1256
+	  300	 bps -> 837
+	  600	 bps -> 417
+	  1200   bps -> 208
+	  1800   bps -> 138
+	  2400   bps -> 103
+	  4800   bps -> 50
+	  9600   bps -> 24
+	  19200  bps -> 11
+	  31500  bps -> 6 (really 31461 bps)
+	  50000  bps -> 3
+	  125000 bps -> 0
+
+*/
+
+
+/* Is channel A switchable between two hardware ports? (Serial2/LAN) */
+
+#define	SCCA_SWITCH_SERIAL2_ONLY	0	/* only connected to Serial2 */
+#define	SCCA_SWITCH_LAN_ONLY		1	/* only connected to LAN */
+#define	SCCA_SWITCH_BOTH		2	/* connected to both, switch by
+						 * IO7 in the PSG */
+
+static int SCC_chan_a_switchable;
+
+/* Is channel A (two ports!) already open? */
+static int SCC_chan_a_open;
+
+/* For which line has channel A been opened? */
+static int SCC_chan_a_line;
+
+/* info pointer for SCC_chan_a_line */
+static struct m68k_async_struct *SCC_chan_a_info;
+
+/* Are the register addresses for the channels reversed? (B before A). This is
+ * the case for the ST_ESCC. */
+static int ChannelsReversed;
+
+/* This macro sets up the 'info' pointer for the interrupt functions of
+ * channel A. It addresses the following problem: The isrs were registered
+ * with callback_data == &rs_table[3] (= Serial2). But they can also be for
+ * &rs_table[4] (LAN), if this port is the open one. SETUP_INFO() thus
+ * advances the pointer if info->line == 3.
+ */
+
+#define DEFAULT_CHANNEL_B_LINE		1 /* ttyS1 */
+#define DEFAULT_CHANNEL_A232_LINE	3 /* ttyS3 */
+#define DEFAULT_CHANNEL_A422_LINE	4 /* ttyS4 */
+
+static int chb_line = -1, cha232_line = -1, cha422_line = -1;
+
+#ifndef CONFIG_ATARI_SCC_DMA
+#define scca_dma  0        /* No DMA support */
+#else
+static int scca_dma = 0;   /* whether DMA is supported at all */
+
+/* ++TeSche: these next few things are for DMA support on channel A. both
+ * BUFFERS and BUFSIZE must be a power of two (because of speed reasons)!
+ */
+#define SCCA_DMA_BUFFERS 8
+#define SCCA_DMA_BUFSIZE 2048
+typedef struct {
+	u_char *buf, *pbuf, *err;
+	int inbuf;
+	short cntOver, cntPar, cntFrame;
+	unsigned active:1;   /* whether a DMA transfer is using this buffer */
+	unsigned needsFlushing:1;   /* whether it's dirty (even when inbuf==0) */
+} DMABUF;
+static DMABUF scca_dma_buf[SCCA_DMA_BUFFERS];
+static DMABUF *scca_dma_head, *scca_dma_tail;
+static DMABUF *scca_dma_end = &scca_dma_buf[SCCA_DMA_BUFFERS];
+
+#endif
+
+#define	SETUP_INFO(info)						\
+	do {										\
+		if (info->line == cha232_line)			\
+			info = SCC_chan_a_info;				\
+	} while(0)
+
+
+/***************************** Prototypes *****************************/
+
+#ifdef ENABLE_ATARI_SCC
+static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_MVME147_SCC
+static void m147_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_MVME162_SCC
+static void mvme_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_BVME6000_SCC
+static void bvme_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef MODULE
+static void SCC_deinit_port( struct m68k_async_struct *info, int channel );
+#endif
+static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);
+#ifdef ENABLE_ATARI_SCC
+static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);
+#endif
+static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct
+                           *tty, struct file *file );
+static void SCC_init( struct m68k_async_struct *info );
+static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,
+				    int divisor );
+static void SCC_change_speed( struct m68k_async_struct *info );
+static int SCC_clocksrc( unsigned baud_base, unsigned channel );
+static void SCC_throttle( struct m68k_async_struct *info, int status );
+static void SCC_set_break( struct m68k_async_struct *info, int break_flag );
+static void SCC_get_serial_info( struct m68k_async_struct *info, struct
+				serial_struct *retinfo );
+static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );
+static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int
+			      new_rts );
+static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
+		     m68k_async_struct *info, unsigned int cmd, unsigned long
+		     arg );
+static void SCC_stop_receive (struct m68k_async_struct *info);
+static int SCC_trans_empty (struct m68k_async_struct *info);
+#ifdef CONFIG_ATARI_SCC_DMA
+static void SCC_timer_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_dma_int (int irq, void *data, struct pt_regs *fp);
+#endif
+
+/************************* End of Prototypes **************************/
+
+
+static SERIALSWITCH SCC_switch = {
+	SCC_init, SCC_deinit, SCC_enab_tx_int,
+	SCC_check_custom_divisor, SCC_change_speed,
+	SCC_throttle, SCC_set_break,
+	SCC_get_serial_info, SCC_get_modem_info,
+	SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,
+	SCC_check_open
+};
+
+extern int atari_SCC_init_done;
+extern int atari_SCC_reset_done;
+
+
+#ifdef CONFIG_BVME6000_SCC
+
+int bvme_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_BVME6000)
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_BVME_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 8;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_BVME;
+	req.port = BVME_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		bvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_BVME;
+	req.port = BVME_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		bvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void bvme_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX,
+		            SCC_tx_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT,
+		            SCC_stat_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX,
+		            SCC_rx_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, BVME_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_bvme;
+		SCC_baud_table[CHANNEL_B] = bdtab_bvme;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_BVME;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_BVME;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+
+#ifdef CONFIG_MVME147_SCC
+
+int m147_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_MVME147)
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_M147_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 2;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_MVME;
+	req.port = M147_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		m147_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_MVME;
+	req.port = M147_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		m147_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+        /*
+         * Ensure interrupts are enabled in the PCC chip
+         */
+        m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB;
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void m147_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX,
+		            SCC_tx_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT,
+		            SCC_stat_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX,
+		            SCC_rx_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, MVME147_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_m147;
+		SCC_baud_table[CHANNEL_B] = bdtab_m147;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_M147;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_M147;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef CONFIG_MVME162_SCC
+
+int mvme_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_MVME16x || !(mvme16x_config & MVME16x_CONFIG_GOT_SCCA))
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_MVME_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 4;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_MVME;
+	req.port = MVME_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		mvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_MVME;
+	req.port = MVME_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		mvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+        /*
+         * Ensure interrupts are enabled in the MC2 chip
+         */
+        *(volatile char *)0xfff4201d = 0x14;
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void mvme_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX,
+		            SCC_tx_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT,
+		            SCC_stat_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX,
+		            SCC_rx_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, MVME162_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_mvme;
+		SCC_baud_table[CHANNEL_B] = bdtab_mvme;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_MVME;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_MVME;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef ENABLE_ATARI_SCC
+
+int atari_SCC_init( void )
+{
+	struct serial_struct req;
+	int escc = ATARIHW_PRESENT(ST_ESCC);
+	int nr = 0;
+	extern char m68k_debug_device[];
+
+	/* SCC present at all? */
+	if (!(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC)))
+		return( -ENODEV );
+
+	scc_del = &mfp.par_dt_reg;
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	/* strengthen the condition a bit to be on the safer side...
+	 */
+	scca_dma = ATARIHW_PRESENT(SCC_DMA) && ATARIHW_PRESENT (TT_MFP);
+#endif
+
+	/* Channel A is switchable on the TT, MegaSTE and Medusa (extension), i.e.
+	 * all machines with an SCC except the Falcon. If there's a machine where
+	 * channel A is fixed to a RS-232 Serial2, add code to set to
+	 * SCCA_SWITCH_SERIAL2_ONLY.
+	 */
+	if (MACH_IS_FALCON)
+		SCC_chan_a_switchable = SCCA_SWITCH_LAN_ONLY;
+	else if (ATARIHW_PRESENT(TT_MFP) || MACH_IS_MSTE)
+		SCC_chan_a_switchable = SCCA_SWITCH_BOTH;
+	else
+		SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+
+	/* General initialization */
+	ChannelsReversed = escc ? 4 : 0;
+	SCC_chan_a_open = 0;
+
+	/* Init channel B */
+	if (!strcmp( m68k_debug_device, "ser2" ))
+		printk(KERN_NOTICE "SCC channel B: used as debug device\n" );
+	else {
+		req.line = DEFAULT_CHANNEL_B_LINE;
+		req.type = SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_b_ctrl : &scc.cha_b_ctrl);
+		if ((chb_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+	}
+
+	/* Init channel A, RS232 part (Serial2) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_LAN_ONLY) {
+		req.line = DEFAULT_CHANNEL_A232_LINE;
+		req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha232_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	/* Init channel A, RS422 part (LAN) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_SERIAL2_ONLY) {
+		req.line = DEFAULT_CHANNEL_A422_LINE;
+		req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha422_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha422_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 * All interrupts are of type PRIORITIZED, which means they can be
+	 * interrupted by all level 6 ints, but not by another SCC (or other level
+	 * 5) int. I see no races with any MFP int, but I'm not quite sure yet
+	 * whether longer delays in between the two-stage SCC register access can
+	 * break things...
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX,
+		            SCC_tx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT,
+		            SCC_stat_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX,
+		            SCC_rx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+		if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+			request_irq(IRQ_TT_MFP_RI, SCC_ri_int, IRQ_TYPE_SLOW,
+			            "TT-MFP ring indicator (modem 2)", info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && !ch_a_inited && type == SER_SCC_DMA && scca_dma) {
+
+			int i, size = SCCA_DMA_BUFFERS * SCCA_DMA_BUFSIZE;
+
+			if (!(scca_dma_buf[0].err = kmalloc (size, GFP_KERNEL))) {
+				printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n");
+				scca_dma = 0;
+			}
+
+			if (scca_dma) {
+				size = (size + PAGE_SIZE - 1) >> 12;
+				if (!(scca_dma_buf[0].buf = (u_char *)__get_dma_pages (GFP_KERNEL, size))) {
+					printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n");
+					kfree (scca_dma_buf[0].err);
+					scca_dma = 0;
+				}
+			}
+
+			if (scca_dma)
+			  if (tt_mfp.int_en_a & tt_mfp.int_mk_a & 0x20) {
+				  printk ("SCC-A: TT_MFP Timer A already in use, DMA support disabled\n");
+				  free_pages ((unsigned long)scca_dma_buf[0].buf, size);
+				  kfree (scca_dma_buf[0].err);
+				  scca_dma = 0;
+			  }
+
+			if (scca_dma) {
+
+				printk ("SCC-A: using %d buffers a %d bytes for DMA\n", SCCA_DMA_BUFFERS, SCCA_DMA_BUFSIZE);
+
+				size = SCCA_DMA_BUFSIZE;
+				for (i=1; i<SCCA_DMA_BUFFERS; i++) {
+					scca_dma_buf[i].buf = scca_dma_buf[0].buf + size;
+					scca_dma_buf[i].err = scca_dma_buf[0].err + size;
+					size += SCCA_DMA_BUFSIZE;
+				}
+
+				for (i=0; i<SCCA_DMA_BUFFERS; i++)
+				  scca_dma_buf[i].pbuf = (char *)virt_to_phys(scca_dma_buf[i].buf);
+
+				tt_mfp.int_en_a &= ~0x20;
+				tt_mfp.int_pn_a = ~0x20;
+				tt_mfp.tim_ct_a = 0x00;
+				tt_mfp.tim_dt_a = 0x00;
+				request_irq (IRQ_TT_MFP_TIMA, SCC_timer_int, IRQ_TYPE_SLOW,
+							 "SCC-A Timer", info);
+				tt_mfp.int_mk_a |= 0x20;
+				tt_mfp.int_en_a |= 0x20;
+				tt_mfp.tim_ct_a = 0x07;
+
+				/* timer now runs with 1/200 pre-divisor and 1/256 data
+				 * counter, say 1/51200 at 2.4576 MHz, say 48 Hz. seems to be
+				 * the slowest frequency possible.
+				 */
+				request_irq (IRQ_TT_MFP_SCC, SCC_dma_int, IRQ_TYPE_PRIO,
+							 "SCC-A DMA", info);
+			}
+		}
+#endif
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Before accessing the SCC the first time, do a read to the
+		 * control register to reset the internal pointers
+		 */
+		SCCread( STATUS_REG );
+
+		if (!atari_SCC_reset_done) {
+			/* The master reset with additional delay */
+			SCCwrite( MASTER_INT_CTRL, MIC_HARD_RESET );
+			udelay(40);
+			atari_SCC_reset_done = 1;
+		}
+
+		/* Set the interrupt vector, 0x60 is standard Atari */
+		SCCwrite( INT_VECTOR_REG, 0x60 );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* to do only once, too: If a TT-MFP is present, initialize its timer
+		 * C that is connected to RTxC of channel B */
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			/* If on a TT, program the TT-MFP's timer C to 307200 kHz
+			 * for RTxC on channel B
+			 */
+			tt_mfp.tim_ct_cd = (tt_mfp.tim_ct_cd & ~0x70) | 0x10; /* 1:4 mode */
+			tt_mfp.tim_dt_c  = 1;
+			/* make sure the timer interrupt is disabled, the timer is used
+			 * only for generating a clock */
+			atari_turnoff_irq( IRQ_TT_MFP_TIMC );
+
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_TTChB;
+
+			/* The clocks are already initialzed for the TT. */
+		}
+		else {
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_norm;
+
+			/* Set the clocks; only RTxCB is different compared to the TT */
+			SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+		}
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef MODULE
+static void SCC_deinit_port( struct m68k_async_struct *info, int channel )
+{
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+		free_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX, info);
+		free_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT, info);
+		free_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX, info);
+		free_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND, info);
+		if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+			free_irq(IRQ_TT_MFP_RI, info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && scca_dma) {
+			tt_mfp.int_en_a &= ~0x20;
+			tt_mfp.int_pn_a = ~0x20;
+			tt_mfp.int_mk_a &= ~0x20;
+			free_irq(IRQ_TT_MFP_SCC, info);
+			free_irq(IRQ_TT_MFP_TIMA, info);
+			free_pages ((unsigned long)scca_dma_buf[0].buf,
+				(SCCA_DMA_BUFFERS * SCCA_DMA_BUFSIZE + PAGE_SIZE - 1) >> 12);
+			kfree (scca_dma_buf[0].err);
+		}
+#endif
+	}
+#endif
+#ifdef CONFIG_MVME147
+	if (MACH_IS_MVME147) {
+		free_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+#ifdef CONFIG_MVME16x
+	if (MACH_IS_MVME16x) {
+		free_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+#ifdef CONFIG_BVME6000
+	if (MACH_IS_BVME6000) {
+		free_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX, info);
+		free_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT, info);
+		free_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX, info);
+		free_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+}
+#endif
+
+
+#ifdef CONFIG_ATARI_SCC_DMA
+
+/*****************************************************************************/
+
+/*
+ * TeSche's high-speed debugging helpers. it has proven (not only at this
+ * place) that ordinary printk()s cause much more problems than they solve when
+ * debugging interrupt handlers. these ones are not nice, but fast.
+ */
+
+#if DEBUG & DEBUG_DMA
+
+#define DEBUGBUFSIZE 1024
+static char debugBuf[DEBUGBUFSIZE];
+static char *debugPtr = &debugBuf[0];
+static char *debugEndPtr = &debugBuf[DEBUGBUFSIZE-1];
+
+static inline void debugString (char *s)
+{
+	while (*s && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *s++;
+}
+
+static inline void debugInt (int i)
+{
+	char *tmp = "0123456789";   /* maxint (unsigned) = 4294967296, 10 digits */
+	short cnt = 10;
+
+	while (--cnt > 0) {
+		tmp[cnt] = '0' + (i % 10);
+		i /= 10;
+	}
+
+	while (*tmp == '0')
+	  tmp++;
+
+	while (*tmp && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *tmp++;
+}
+
+static char int2hex[] = "0123456789abcdef";
+
+static inline void debugHex (unsigned int h)
+{
+	char *tmp = "01234567";
+	short cnt = 8;
+
+	while (--cnt > 0) {
+		tmp[cnt] = int2hex[h & 15];
+		h >>= 4;
+	}
+
+	while (*tmp == '0')
+	  tmp++;
+
+	while (*tmp && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *tmp++;
+}
+
+static inline void debugFlush (void)
+{
+	if (debugPtr != &debugBuf[0]) {
+		*debugPtr = 0;
+		printk ("%s\n", debugBuf);
+		debugPtr = &debugBuf[0];
+	}
+}
+
+#endif /* DEBUG & DEBUG_DMA */
+
+
+/*****************************************************************************/
+
+/*
+ * these functions are for DMA support. they all assume that they're called
+ * with INTs off so that they can play with their data structures undisturbed.
+ */
+
+static ulong dmaStartAddr = 0;   /* 0 means DMA not running */
+static ulong dmaSize;
+
+
+/* start DMA on current (scca_head) write buffer
+ */
+static inline void dma_start (void)
+{
+	if ((dmaSize = SCCA_DMA_BUFSIZE - scca_dma_head->inbuf) > 0) {
+		dmaStartAddr = (ulong)(scca_dma_head->pbuf + scca_dma_head->inbuf); /* needs no virt_to_phys() */
+#if DEBUG & DEBUG_DMA
+		debugString ("[start@0x");
+		debugHex (dmaStartAddr & 0xffff);
+		debugString ("/");
+		debugInt (dmaSize);
+		debugString ("] ");
+#endif
+		tt_scc_dma.dma_ctrl &= ~3;
+		__asm__ __volatile__ ("movep.l %0,%1@(0)\n\t"
+							  "movep.l %2,%3@(0)\n\t"
+							  : /* no outputs */
+							  : "d"(dmaStartAddr), "a"(&tt_scc_dma.dma_addr_hi),
+							  "d"(dmaSize), "a"(&tt_scc_dma.dma_cnt_hi)
+							  : "memory");
+		tt_scc_dma.dma_ctrl |= 2;
+	} else {
+		dmaStartAddr = 0;
+	}
+}
+
+
+/* stop DMA, read restbytes and adjust buffer counters
+ */
+static inline void dma_stop (void)
+{
+	register short rest;
+	ulong size = 0, endaddr;
+	unsigned char *from = (unsigned char *)&tt_scc_dma.dma_restdata, *to;
+
+	if (!dmaStartAddr)
+	  return;
+
+	tt_scc_dma.dma_ctrl &= ~3;   /* stop DMA */
+
+	/* ++TeSche: I've had tremendous problems with looking at dma_addr to see
+	 * how many bytes were received rather than looking at dma_cnt. To me it
+	 * looks like there are cases when dma_addr is not properly updated when
+	 * DMA is aborted, so I ended up with calculating less bytes than actually
+	 * were received. Dma_cnt seems to be ok for me, so this is the way I go.
+	 *
+	 * sigh, yet one more unspecified hardware feature? is it at all anywhere
+	 * specified what happens when a DMA is aborted before it ends?
+	 */
+	__asm__ __volatile__ ("movep.l %1@(0),%0\n\t"
+						  : "=d"(size)
+						  : "a"(&tt_scc_dma.dma_cnt_hi)
+						  : "memory");
+	size = dmaSize - size;
+
+#if DEBUG & DEBUG_DMA
+	__asm__ __volatile__ ("movep.l %1@(0),%0\n\t"
+						  : "=d"(endaddr)
+						  : "a"(&tt_scc_dma.dma_addr_hi)
+						  : "memory");
+	if (endaddr - dmaStartAddr != size) {
+		debugString ("[size=");
+		debugInt (size);
+		debugString (",addr=");
+		debugInt (endaddr-dmaStartAddr);
+		debugString ("] ");
+	}
+#endif
+
+	endaddr = dmaStartAddr + size;
+
+	if ((dmaStartAddr & ~3) != (endaddr & ~3)) {
+		/* at least one long was written. lower two bits of endaddress are
+		 * number of restbytes. write them left-justified to the long the
+		 * endaddress is in.
+		 */
+		rest = endaddr & 3;
+		to = scca_dma_head->buf + (endaddr & (SCCA_DMA_BUFSIZE-1) & ~3);   /* needs no PTOV */
+	} else {
+		/* no long written. number of restbytes is endaddress -
+		 * startaddress. write them to the startaddress.
+		 */
+		rest = size;   /* must and will be 0..3 */
+		from += dmaStartAddr & 3;
+		to = scca_dma_head->buf + (dmaStartAddr & (SCCA_DMA_BUFSIZE-1));   /* needs no PTOV */
+	}
+
+#if DEBUG & DEBUG_DMA
+	debugString ("[stop@0x");
+	debugHex (endaddr & 0xffff);
+	debugString ("/");
+	debugInt (size);
+	if (rest) {
+		debugString (",rest=");
+		debugInt (rest);
+		debugString ("@0x");
+		debugHex ((unsigned int)from);
+		debugString ("->");
+		debugHex (((unsigned int)to) & 0xffff);
+	}
+	debugString ("] ");
+#endif
+
+	while (--rest >= 0)
+	  *to++ = *from++;
+
+	scca_dma_head->inbuf += size;
+}
+
+
+/* used by the SPCOND INT handler to deliver error codes. it's not very clean,
+ * maybe overwrites older values, but since that only happens when something
+ * has already gone wrong I don't consider this a problem.
+ */
+static inline void dma_fake_receive (u_char data, u_char err)
+{
+	scca_dma_head->buf[scca_dma_head->inbuf] = data;
+	scca_dma_head->err[scca_dma_head->inbuf] = err;
+
+	if (scca_dma_head->inbuf < SCCA_DMA_BUFSIZE-1)
+	  scca_dma_head->inbuf++;
+
+	switch (err) {
+	  case TTY_OVERRUN:
+		scca_dma_head->cntOver++;
+		break;
+	  case TTY_PARITY:
+		scca_dma_head->cntPar++;
+		break;
+	  case TTY_FRAME:
+		scca_dma_head->cntFrame++;
+	}
+}
+
+
+/*****************************************************************************/
+
+/*
+ * these functions are for high-level DMA support. they make no assumptions
+ * about current INT status when called.
+ */
+
+/* can't be called more than once due to tqueue handling + clever (?:) variable
+ * design -> no cli/sti.
+ */
+static void SCC_flush (struct tty_struct *tty)
+{
+	int loops = 0;
+	static int highWater = (95 * SCCA_DMA_BUFSIZE) / 100;
+
+	/* a potential endless loop, but that's *really* unlikely...
+	 */
+	while (scca_dma_tail->needsFlushing) {
+
+		/* ...anyway, be save
+		 */
+		if (++loops > SCCA_DMA_BUFFERS) {
+			printk ("SCC-A: flush loop overrun\n");
+			break;
+		}
+
+#if DEBUG & DEBUG_DMA
+		if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame) {
+			debugString ("[ovr=");
+			debugInt (scca_dma_tail->cntOver);
+			debugString (",par=");
+			debugInt (scca_dma_tail->cntPar);
+			debugString (",frm=");
+			debugInt (scca_dma_tail->cntFrame);
+			debugString ("] ");
+		}
+#else
+		if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame)
+		  printk ("SCC-A: %d overrun, %d parity, %d frame errors\n",
+				  scca_dma_tail->cntOver, scca_dma_tail->cntPar, scca_dma_tail->cntFrame);
+#endif
+
+		if (scca_dma_tail->inbuf > highWater)
+		  printk ("SCC-A: warning: buffer usage: %d/%d chars\n", scca_dma_tail->inbuf, SCCA_DMA_BUFSIZE);
+
+#if 0
+		if (scca_dma_tail->inbuf > 1) {
+			scca_dma_tail->buf[0] |= 0x1;
+			scca_dma_tail->buf[scca_dma_tail->inbuf-1] |= 0x2;
+		}
+#endif
+
+		tty->ldisc.receive_buf (tty, scca_dma_tail->buf, scca_dma_tail->err, scca_dma_tail->inbuf);
+
+		scca_dma_tail->cntOver = 0;
+		scca_dma_tail->cntPar = 0;
+		scca_dma_tail->cntFrame = 0;
+		scca_dma_tail->inbuf = 0;
+		scca_dma_tail->needsFlushing = 0;
+
+		if (++scca_dma_tail == scca_dma_end)
+		  scca_dma_tail = scca_dma_buf;
+	}
+
+#if DEBUG & DEBUG_DMA
+	debugFlush ();
+#endif
+}
+
+
+static struct tq_struct SCC_flush_tqueue = {
+	NULL,		/* next */
+	0,			/* sync */
+	(void (*)(void*)) SCC_flush,  /* routine, must have (void *) arg... */
+	NULL		/* data */
+};
+
+
+/* the 48Hz timer to flush data. runs in fact only every 4th call, say at 12Hz.
+ */
+static void SCC_timer_int (int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	static int delay = 4;
+	ulong flags;
+	SCC_ACCESS_INIT(info);
+
+	/* if 'fp' is NULL we're called from SCC_dma_int, in which case we must
+	 * respond immediately!
+	 */
+	if (fp && --delay > 0)
+	  return;
+
+#if DEBUG & DEBUG_DMA
+	delay = 100;   /* 0.48Hz for better debugging, no more than 19k2b! */
+#else
+	delay = 4;   /* 12Hz delivery frequency (960 bytes / delivery @ 115k2b) */
+#endif
+
+	if (!SCC_flush_tqueue.data)
+	  return;   /* no program listening... */
+
+	save_flags (flags);
+	cli ();
+
+	if (scca_dma_head->active) {
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0x00);
+		dma_stop ();
+		scca_dma_head->needsFlushing = 1;
+		scca_dma_head->active = 0;
+		if (++scca_dma_head == scca_dma_end)
+		  scca_dma_head = scca_dma_buf;
+	}
+
+	if (!scca_dma_head->needsFlushing) {
+		scca_dma_head->active = 1;
+		dma_start ();
+		SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+		/* this must *happen* after re-starting DMA for speed reasons.
+		 */
+		memset (scca_dma_head->err, 0x00, SCCA_DMA_BUFSIZE);
+	} else {
+		printk ("SCC-A: fatal buffer overflow, data lost!\n");
+	}
+
+	queue_task (&SCC_flush_tqueue, &tq_immediate);
+	mark_bh (IMMEDIATE_BH);
+
+	restore_flags (flags);
+}
+
+
+/* DMA finished before timer occured?
+ */
+static void SCC_dma_int (int irq, void *data, struct pt_regs *fp)
+{
+	printk ("SCC-A: DMA-INT occured, data lost!\n");
+#if 0
+	/* is there any reason why we should call this? if the timer INT was
+	 * delayed so long that this happened then this INT was delayed too, so
+	 * it's already too late.
+	 */
+	SCC_timer_int (irq, (struct m68k_async_struct *)data, NULL);
+#endif
+}
+
+/*****************************************************************************/
+
+#endif
+
+
+#if DEBUG & DEBUG_OVERRUNS
+static int SCC_ch_cnt[2] = { 0, 0 }, SCC_ch_ovrrun[2] = { 0, 0 };
+#endif
+
+static void SCC_rx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned char	ch;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	ch = SCCread_NB( RX_DATA_REG );
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d rx int: char %02x\n", CHANNR(info), ch );
+#endif
+	rs_receive_char (info, ch, 0);
+#if DEBUG & DEBUG_OVERRUNS
+	{ int channel = CHANNR(info);
+	  if (++SCC_ch_cnt[channel] == 10000) {
+		  printk( "SCC ch. %d: overrun rate %d.%02d\n", channel,
+				  SCC_ch_ovrrun[channel] / 100,
+				  SCC_ch_ovrrun[channel] % 100 );
+		  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+	  }
+	}
+#endif
+
+	/* Check if another character is already ready; in that case, the
+	 * spcond_int() function must be used, because this character may have an
+	 * error condition that isn't signalled by the interrupt vector used!
+	 */
+	if (SCCread( INT_PENDING_REG ) &
+	    (CHANNR(info) == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
+		SCC_spcond_int (0, info, 0);
+		return;
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_spcond_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned char	stat, ch, err;
+	int		int_pending_mask = CHANNR(info) == CHANNEL_A ?
+			                   IPR_A_RX : IPR_B_RX;
+#ifdef CONFIG_ATARI_SCC_DMA
+	int isdma = (CHANNR(info) == CHANNEL_A) && scca_dma;
+	ulong flags = 0;
+#endif
+	SCC_ACCESS_INIT(info);
+	
+	SETUP_INFO(info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (isdma) {
+		save_flags (flags);
+		cli ();
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0);
+		dma_stop ();
+	}
+#endif
+
+	do {
+		stat = SCCread( SPCOND_STATUS_REG );
+		ch = SCCread_NB(RX_DATA_REG);
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch %d spcond int: char %02x stat %02x\n",
+			   CHANNR(info), ch, stat );
+#endif
+
+		if (stat & SCSR_RX_OVERRUN)
+			err = TTY_OVERRUN;
+		else if (stat & SCSR_PARITY_ERR)
+			err = TTY_PARITY;
+		else if (stat & SCSR_CRC_FRAME_ERR)
+			err = TTY_FRAME;
+		else
+			err = 0;
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (isdma)
+		  dma_fake_receive (ch, err);
+		else
+#endif
+		  rs_receive_char (info, ch, err);
+
+		/* ++TeSche: *All* errors have to be cleared manually,
+		 * else the condition persists for the next chars
+		 */
+		if (err)
+		  SCCwrite(COMMAND_REG, CR_ERROR_RESET);
+
+#if DEBUG & DEBUG_OVERRUNS
+		{ int channel = CHANNR(info);
+		  if (err == TTY_OVERRUN) SCC_ch_ovrrun[channel]++;
+		  if (++SCC_ch_cnt[channel] == 10000) {
+			  printk( "SCC ch. %d: overrun rate %d.%02d %%\n", channel,
+					  SCC_ch_ovrrun[channel] / 100,
+					  SCC_ch_ovrrun[channel] % 100 );
+			  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+		  }
+	    }
+#endif
+
+	} while( SCCread( INT_PENDING_REG ) & int_pending_mask );
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (isdma) {
+		dma_start ();
+		SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+		restore_flags (flags);
+	}
+#endif
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+#ifdef ENABLE_ATARI_SCC
+static void SCC_ri_int(int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	/* update input line counter */
+	info->icount.rng++;
+	wake_up_interruptible(&info->delta_msr_wait);
+}
+#endif
+
+
+static void SCC_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	int ch;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	while( (SCCread_NB( STATUS_REG ) & SR_TX_BUF_EMPTY) &&
+		   (ch = rs_get_tx_char( info )) >= 0 ) {
+		SCCwrite( TX_DATA_REG, ch );
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch. %d tx int: sent char %02x\n", CHANNR(info), ch );
+#endif
+	}
+
+	if (rs_no_more_tx( info )) {
+		/* disable tx interrupts */
+		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+		SCCwrite( COMMAND_REG, CR_TX_PENDING_RESET );   /* disable tx_int on next tx underrun? */
+#if DEBUG & DEBUG_INT
+		printk ("SCC ch %d tx int: no more chars after %d sent\n",
+				CHANNR (info), total);
+#endif
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_stat_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned channel = CHANNR(info);
+	unsigned char	last_sr, sr, changed;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	last_sr = SCC_last_status_reg[channel];
+	sr = SCC_last_status_reg[channel] = SCCread_NB( STATUS_REG );
+	changed = last_sr ^ sr;
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d stat int: sr=%02x last_sr=%02x\n",
+			CHANNR(info), sr, last_sr );
+#endif
+
+	if (changed & SR_DCD)
+		rs_dcd_changed( info, sr & SR_DCD );
+
+	if (changed & SR_CTS) {
+#if DEBUG & DEBUG_THROTTLE
+		printk( "SCC ch. %d: now CTS=%d\n", CHANNR(info), !!(sr & SR_CTS) );
+#endif
+		rs_check_cts( info, sr & SR_CTS );
+	}
+
+	if (changed & SR_SYNC_ABORT) { /* Data Set Ready */
+		/* update input line counter */
+		info->icount.dsr++;
+		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	SCCwrite( COMMAND_REG, CR_EXTSTAT_RESET );
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty,
+			  struct file *file )
+{
+	/* If channel A is opened, check if one of the compounded ports (ttyS3 and
+	 * ttyS4) is already open, else activate the appropriate port hardware.
+	 */
+
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC: about to open channel %d as line %d\n",
+			CHANNR(info), info->line );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A) {
+
+		if (SCC_chan_a_open) {
+			if (SCC_chan_a_line != info->line) {
+#if DEBUG & DEBUG_OPEN
+				printk("SCC: channel 0 was already open\n");
+#endif
+				return -EBUSY;
+			}
+			else
+				return 0;
+		}
+
+		if ((info->line == cha232_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_LAN_ONLY) ||
+		    (info->line == cha422_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_SERIAL2_ONLY))
+			return( -ENODEV );
+
+		SCC_chan_a_open = 1;
+		SCC_chan_a_line = info->line;
+		SCC_chan_a_info = &rs_table[info->line];
+#ifdef ENABLE_ATARI_SCC
+		if (SCC_chan_a_switchable == SCCA_SWITCH_BOTH) {
+			unsigned long flags; 
+			unsigned char tmp;
+
+			save_flags(flags);
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			tmp = sound_ym.rd_data_reg_sel;
+			sound_ym.wd_data = (info->line == cha232_line
+					    ? tmp | 0x80
+					    : tmp & 0x7f);
+#if DEBUG & DEBUG_OPEN
+			printk( "SCC: set PSG IO7 to %02x (was %02x)\n",
+			       (info->line & 1) ? (tmp | 0x80) : (tmp & 0x7f),
+			       tmp );
+#endif
+			restore_flags(flags);
+		}
+#endif
+	}
+	return( 0 );
+}
+
+
+static void SCC_init( struct m68k_async_struct *info )
+{
+	int i, channel = CHANNR(info);
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+#ifdef ENABLE_ATARI_SCC
+	static const struct {
+		unsigned reg, val;
+	} init_tab[] = {
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
+		{ CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
+		{ DPLL_CTRL_REG, 0 },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#ifdef CONFIG_ATARI_SCC_DMA
+	static const struct {
+		unsigned reg, val;
+	} init_withdma_tab[] = {
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
+		/* parity error is special cond, ints disabled, DMA receive but disabled */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB |
+			IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ},
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
+		{ CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
+		{ DPLL_CTRL_REG, 0 },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* parity error is special cond, Tx & SPcond ints enabled, Rx int disabled, DMA receive but disabled */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | IDR_PARERR_AS_SPCOND |
+			IDR_RX_INT_DISAB | IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ}
+	};
+#endif
+#endif
+#ifdef CONFIG_MVME147_SCC
+	static const struct {
+		unsigned reg, val;
+	} m147_init_tab[] = {
+		/* Values for MVME147 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+#ifdef CONFIG_MVME162_SCC
+	static const struct {
+		unsigned reg, val;
+	} mvme_init_tab[] = {
+		/* Values for MVME162 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+#ifdef CONFIG_BVME6000_SCC
+	static const struct {
+		unsigned reg, val;
+	} bvme_init_tab[] = {
+		/* Values for BVME6000 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+	save_flags(flags);
+	cli();
+
+	if (!MACH_IS_MVME16x && !MACH_IS_BVME6000 && !MACH_IS_MVME147) {
+		SCCmod( MASTER_INT_CTRL, 0x3f,
+			channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET );
+		udelay(40); /* extra delay after a reset */
+	}
+
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && scca_dma) {
+
+			for (i=0; i<sizeof(init_withdma_tab)/sizeof(*init_withdma_tab); ++i)
+				  SCCwrite( init_withdma_tab[i].reg, init_withdma_tab[i].val );
+
+			/* enable SCC-DMA INTs
+			 */
+			tt_mfp.int_en_b &= ~4;
+			tt_mfp.active_edge |= 4;
+			tt_mfp.int_pn_b = ~4;
+			tt_mfp.int_mk_b |= 4;
+			tt_mfp.int_en_b |= 4;
+
+			/* init and start dma
+			 */
+			scca_dma_head = scca_dma_tail = scca_dma_buf;
+			for (i=0; i<SCCA_DMA_BUFFERS; i++) {
+				scca_dma_buf[i].inbuf = 0;
+				scca_dma_buf[i].cntOver = 0;
+				scca_dma_buf[i].cntPar = 0;
+				scca_dma_buf[i].cntFrame = 0;
+				scca_dma_buf[i].active = 0;
+				scca_dma_buf[i].needsFlushing = 0;
+				memset (scca_dma_buf[i].err, 0, SCCA_DMA_BUFSIZE);
+			}
+
+			scca_dma_head->active = 1;
+			dma_start ();
+			SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+
+			SCC_flush_tqueue.data = ((struct m68k_async_struct *)info)->tty;
+
+		} else
+#endif
+		{
+			for (i=0; i<sizeof(init_tab)/sizeof(*init_tab); ++i)
+				SCCwrite( init_tab[i].reg, init_tab[i].val );
+		}
+	}
+#endif
+#ifdef CONFIG_MVME147_SCC
+	if (MACH_IS_MVME147) {
+		for (i=0; i<sizeof(m147_init_tab)/sizeof(*m147_init_tab); ++i)
+			SCCwrite( m147_init_tab[i].reg, m147_init_tab[i].val );
+	}
+#endif
+#ifdef CONFIG_MVME162_SCC
+	if (MACH_IS_MVME16x) {
+		for (i=0; i<sizeof(mvme_init_tab)/sizeof(*mvme_init_tab); ++i)
+			SCCwrite( mvme_init_tab[i].reg, mvme_init_tab[i].val );
+	}
+#endif
+#ifdef CONFIG_BVME6000_SCC
+	if (MACH_IS_BVME6000) {
+		for (i=0; i<sizeof(bvme_init_tab)/sizeof(*bvme_init_tab); ++i)
+			SCCwrite( bvme_init_tab[i].reg, bvme_init_tab[i].val );
+	}
+#endif
+
+	/* remember status register for detection of DCD and CTS changes */
+	SCC_last_status_reg[channel] = SCCread( STATUS_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d inited\n", CHANNR(info) );
+#endif
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d opened\n", CHANNR(info) );
+#endif
+	MOD_INC_USE_COUNT;
+}
+
+
+static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+	unsigned long	flags, timeout;
+	SCC_ACCESS_INIT(info);
+
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d about to be deinited\n", CHANNR(info) );
+#endif
+
+	if (MACH_IS_MVME147 && CHANNR(info) == CHANNEL_A)
+		return;		/* Channel A is our console */
+	if (MACH_IS_MVME16x && CHANNR(info) == CHANNEL_A)
+		return;		/* 162Bug uses channel A */
+	if (MACH_IS_BVME6000 && CHANNR(info) == CHANNEL_A)
+		return;		/* BVMbug uses channel A */
+
+	save_flags(flags);
+	cli();
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (CHANNR(info) == CHANNEL_A && scca_dma) {
+		SCC_flush_tqueue.data = NULL;
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0);
+		dma_stop ();
+		tt_mfp.int_en_b &= ~4; /* disable TT-MFP INTs */
+		/* ++TeSche: for the moment we don't care if there's still data unflushed */
+	}
+#endif
+
+	/* disable interrupts */
+	SCCmod( INT_AND_DMA_REG, ~(IDR_EXTSTAT_INT_ENAB | IDR_TX_INT_ENAB |
+							   IDR_RX_INT_SPCOND), 0 );
+
+	/* disable Rx */
+	SCCmod( RX_CTRL_REG, ~RCR_RX_ENAB, 0 );
+
+	/* disable Transmitter */
+	SCCmod( TX_CTRL_REG, ~TCR_TX_ENAB, 0 );
+
+	/* wait until character is completely sent */
+	timeout = jiffies + 50;
+	restore_flags(flags);
+	while( !(SCCread( SPCOND_STATUS_REG ) & SCSR_ALL_SENT) ) {
+		if (time_after(jiffies, timeout)) break;
+	}
+	save_flags(flags);
+	cli();
+
+	/* drop RTS and maybe DTR */
+	SCCmod( TX_CTRL_REG, ~(TCR_RTS | (leave_dtr ? 0 : TCR_DTR)), 0 );
+
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d deinited\n", CHANNR(info) );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A)
+		SCC_chan_a_open = 0;
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d closed, chanAlock now = %d\n",
+			CHANNR(info), SCC_chan_a_open );
+#endif
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
+{
+	unsigned long	flags;
+	unsigned char	iadr;
+	SCC_ACCESS_INIT(info);
+
+	save_flags(flags);
+	cli();
+
+	iadr = SCCread( INT_AND_DMA_REG );
+	if (!!(iadr & IDR_TX_INT_ENAB) != !!enab_flag) {
+		SCCwrite(INT_AND_DMA_REG, iadr ^ IDR_TX_INT_ENAB);
+#if DEBUG & DEBUG_INT
+		printk("SCC ch %d: tx int %sabled\n", CHANNR(info),
+		       enab_flag ? "en" : "dis");
+#endif
+		if (enab_flag)
+		  /* restart the transmitter */
+		  SCC_tx_int (0, info, 0);
+	}
+
+	restore_flags(flags);
+}
+
+
+static int SCC_check_custom_divisor( struct m68k_async_struct *info,
+				    int baud_base, int divisor )
+{
+	int		clksrc;
+
+	clksrc = SCC_clocksrc (baud_base, CHANNR (info));
+	if (clksrc < 0)
+		/* invalid baud base */
+		return( -1 );
+
+	/* check for maximum (BRG values start from 4 with step 2) */
+	if (divisor/2-2 > 65535)
+		return( -1 );
+
+	switch( clksrc ) {
+
+	  case CLK_PCLK:
+		/* The master clock can only be used with the BRG, divisors
+		 * range from 4 and must be a multiple of 2
+		 */
+		return( !(divisor >= 4 && (divisor & 1) == 0) );
+
+	  case CLK_RTxC:
+		/* The RTxC clock can either be used for the direct 1:16, 1:32
+		 * or 1:64 modes (divisors 1, 2 or 4, resp.) or with the BRG
+		 * (divisors from 4 and a multiple of 2)
+		 */
+		return( !(divisor >= 1 && (divisor == 1 || (divisor & 1) == 0)) );
+
+	  case CLK_TRxC:
+		/* The TRxC clock can only be used for direct 1:16, 1:32 or
+		 * 1:64 modes
+		 */
+		return( !(divisor == 1 || divisor == 2 || divisor == 4) );
+
+	}
+	return( -1 );
+}
+
+
+static void SCC_change_speed( struct m68k_async_struct *info )
+{
+	/* the SCC has char sizes 5,7,6,8 in that order! */
+	static int chsize_map[4] = { 0, 2, 1, 3 };
+	unsigned cflag, baud, chsize, aflags;
+	unsigned channel, div = 0, clkmode, brgmode, brgval;
+	int clksrc = 0;
+	unsigned long flags;
+	SCC_ACCESS_INIT(info);
+
+	if (!info->tty || !info->tty->termios) return;
+
+	channel = CHANNR(info);
+
+	if (MACH_IS_MVME147 && channel == CHANNEL_A)
+		return;		/* Settings controlled by 147Bug */
+	if (MACH_IS_MVME16x && channel == CHANNEL_A)
+		return;		/* Settings controlled by 162Bug */
+	if (MACH_IS_BVME6000 && channel == CHANNEL_A)
+		return;		/* Settings controlled by BVMBug */
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = (cflag & CSIZE) >> 4;
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+#if DEBUG & DEBUG_SPEED
+	printk( "SCC channel %d: doing new settings:\n", CHANNR(info) );
+	printk( "  baud=%d chsize=%d aflags=%04x base_baud=%d divisor=%d\n",
+			baud, chsize, aflags, info->baud_base, info->custom_divisor );
+#endif
+
+	if (baud == 0 && !aflags) {
+		/* speed == 0 -> drop DTR */
+		save_flags(flags);
+		cli();
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+		restore_flags(flags);
+		return;
+	}
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > (MACH_IS_MVME16x ? 2 : 4))
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15 && aflags) {
+		switch( aflags) {
+		  case ASYNC_SPD_HI:
+			baud = 16;
+			break;
+		  case ASYNC_SPD_VHI:
+			baud = 17;
+			break;
+		  case ASYNC_SPD_SHI:
+			baud = 18;
+			break;
+		  case ASYNC_SPD_WARP:
+			baud = 19;
+			break;
+		  case ASYNC_SPD_CUST:
+			/* Custom divisor: Compute clock source from the base_baud
+			 * field */
+			if ((clksrc = SCC_clocksrc( info->baud_base, channel )) < 0)
+				/* This shouldn't happen... the baud_base has been checked
+				 * before by check_custom_divisor() */
+				return;
+			div = info->custom_divisor;
+		}
+	}
+
+	if (!div) {
+		if (baud > 19) baud = 19;
+		clksrc = SCC_baud_table[channel][baud].clksrc;
+		div = SCC_baud_table[channel][baud].div;
+		if(!div)
+		{
+			printk(" SCC_change_speed: divisor = 0 !!!");
+			return;
+		}
+	}
+
+	/* compute the SCC's clock source, clock mode, BRG mode and BRG
+	 * value from clksrc and div
+	 */
+	if (div <= 4) {
+		clkmode = (div == 1 ? A1CR_CLKMODE_x16 :
+			   div == 2 ? A1CR_CLKMODE_x32 :
+				      A1CR_CLKMODE_x64);
+		clksrc  = (clksrc == CLK_RTxC
+			   ? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC
+			   : CCR_TXCLK_TRxC | CCR_RXCLK_TRxC);
+		brgmode = 0; /* off */
+		brgval  = 0;
+	}
+	else {
+		brgval  = div/2 - 2;
+		brgmode = (DCR_BRG_ENAB |
+			   (clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0));
+		clkmode = A1CR_CLKMODE_x16;
+		clksrc  = CCR_TXCLK_BRG | CCR_RXCLK_BRG;
+	}
+
+	/* Now we have all parameters and can go to set them: */
+	save_flags(flags);
+	cli();
+#if DEBUG & DEBUG_SPEED
+	printk( "  brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n",
+			brgval, brgmode, clkmode, clksrc );
+#endif
+
+	/* receiver's character size */
+	SCCmod( RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize_map[chsize] << 6 );
+#if DEBUG & DEBUG_SPEED
+	printk( "  RX_CTRL_REG <- %02x\n", SCCread( RX_CTRL_REG ) );
+#endif
+
+	/* parity and stop bits (both, Tx and Rx) and clock mode */
+	SCCmod (AUX1_CTRL_REG,
+		~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK),
+		((cflag & PARENB
+		  ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
+		  : A1CR_PARITY_NONE)
+		 | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)
+		 | clkmode));
+#if DEBUG & DEBUG_SPEED
+	printk( "  AUX1_CTRL_REG <- %02x\n", SCCread( AUX1_CTRL_REG ) );
+#endif
+
+	/* sender's character size */
+	/* Set DTR for valid baud rates! Tnx to jds@kom.auc.dk */
+	SCCmod( TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TX_CTRL_REG <- %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	/* clock sources */
+	SCCmod( CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc );
+#if DEBUG & DEBUG_SPEED
+	printk( "  CLK_CTRL_REG <- %02x\n", SCCread( CLK_CTRL_REG ) );
+#endif
+
+	/* disable BRG before changing the value */
+	SCCmod( DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0 );
+
+	/* BRG value */
+	SCCwrite( TIMER_LOW_REG, brgval & 0xff );
+	SCCwrite( TIMER_HIGH_REG, (brgval >> 8) & 0xff );
+
+	/* BRG enable and clock source */
+	SCCmod( DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TIMER_LOW_REG <- %02x\n", SCCread( TIMER_LOW_REG ) );
+	printk( "  TIMER_HIGH_REG <- %02x\n", SCCread( TIMER_HIGH_REG ) );
+#endif
+#if DEBUG & DEBUG_SPEED
+	printk( "  DPLL_CTRL_REG <- %02x\n", SCCread( DPLL_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static int SCC_clocksrc( unsigned baud_base, unsigned channel )
+{
+	if (baud_base == SCC_PCLK)
+		return( CLK_PCLK );
+	else if (SCC_clocks[channel][CLK_RTxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_RTxC])
+		return( CLK_RTxC );
+	else if (SCC_clocks[channel][CLK_TRxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_TRxC])
+		return( CLK_TRxC );
+	else
+		return( -1 );
+}
+
+static void SCC_throttle( struct m68k_async_struct *info, int status )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "SCC channel %d: throttle %s\n",
+	       CHANNR(info), status ? "full" : "avail" );
+#endif
+	save_flags(flags);
+	cli();
+
+	if (status)
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	else
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "  now TX_CTRL_REG = %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static void SCC_set_break( struct m68k_async_struct *info, int break_flag )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+
+	save_flags(flags);
+	cli();
+
+	if (break_flag) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_SEND_BREAK );
+	} else {
+		SCCmod( TX_CTRL_REG, ~TCR_SEND_BREAK, 0 );
+	}
+
+	restore_flags(flags);
+}
+
+
+static void SCC_get_serial_info( struct m68k_async_struct *info,
+				struct serial_struct *retinfo )
+{
+	retinfo->baud_base = info->baud_base;
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int SCC_get_modem_info( struct m68k_async_struct *info )
+{
+	unsigned	sr, tcr, ri = 0, dsr = 0;
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+	
+	save_flags(flags);
+	cli();
+	sr = SCCread( STATUS_REG );
+	tcr = SCCread( TX_CTRL_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: get info, sr=%02x tcr=%02x\n",
+			CHANNR(info), sr, tcr );
+#endif
+#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
+	if (MACH_IS_MVME147 || MACH_IS_MVME16x || MACH_IS_BVME6000) {
+		ri = 0;
+		dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0;
+	}
+#endif
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+		if (CHANNR (info) == 0)
+			ri = 0;
+		else if (ATARIHW_PRESENT (TT_MFP))
+			ri = tt_mfp.par_dt_reg & (1 << 3) ? 0 : TIOCM_RNG;
+		else
+			ri = mfp.par_dt_reg & (1 << 6) ? 0 : TIOCM_RNG;
+
+		if (ATARIHW_PRESENT (ST_ESCC))
+			dsr = st_escc_dsr & (1 << (3 - CHANNR(info))) ? TIOCM_DSR : 0;
+		else
+			dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0;
+	}
+#endif
+	return (((tcr & TCR_RTS) ? TIOCM_RTS : 0) |
+		((tcr & TCR_DTR) ? TIOCM_DTR : 0) |
+		((sr & SR_DCD ) ? TIOCM_CAR : 0) |
+		((sr & SR_CTS ) ? TIOCM_CTS : 0) |
+		dsr | ri);
+}
+
+
+static int SCC_set_modem_info( struct m68k_async_struct *info,
+			      int new_dtr, int new_rts )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+	
+	save_flags(flags);
+	cli();
+
+	if (new_dtr == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+	} else if (new_dtr == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_DTR );
+	}
+
+	if (new_rts == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	} else if (new_rts == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+	}
+
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: set info (dtr=%d,rts=%d), now tcr=%02x\n",
+	       CHANNR(info), new_dtr, new_rts, SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+	return( 0 );
+}
+
+static void SCC_stop_receive (struct m68k_async_struct *info)
+{
+	SCC_ACCESS_INIT(info);
+	
+#ifdef CONFIG_ATARI_SCC_DMA
+	dma_stop ();
+#endif
+
+	/* disable Rx interrupts */
+	SCCmod (INT_AND_DMA_REG, ~IDR_RX_INT_MASK, 0);
+
+	/* disable Rx */
+	if (!((MACH_IS_MVME16x || MACH_IS_BVME6000) && CHANNR(info) == CHANNEL_A))
+		SCCmod (RX_CTRL_REG, ~RCR_RX_ENAB, 0);
+}
+
+static int SCC_trans_empty (struct m68k_async_struct *info)
+{
+	SCC_ACCESS_INIT(info);
+	
+	return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) != 0;
+}
+
+static int SCC_ioctl( struct tty_struct *tty, struct file *file,
+		     struct m68k_async_struct *info, unsigned int cmd,
+		     unsigned long arg )
+{
+	struct atari_SCCserial *cp = (void *)arg;
+	int error;
+	unsigned channel = CHANNR(info), i, clk, div, rtxc, trxc, pclk;
+
+	switch( cmd ) {
+
+	  case TIOCGATSCC:
+
+		error = verify_area( VERIFY_WRITE, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		put_user(SCC_clocks[channel][CLK_RTxC], &cp->RTxC_base);
+		put_user(SCC_clocks[channel][CLK_TRxC], &cp->TRxC_base);
+		put_user(SCC_PCLK, &cp->PCLK_base);
+		copy_to_user(cp->baud_table, SCC_baud_table[channel] + 1,
+			     sizeof(cp->baud_table));
+
+		return( 0 );
+
+	  case TIOCSATSCC:
+
+		if (!suser()) return( -EPERM );
+
+		error = verify_area(VERIFY_READ, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		get_user(rtxc, &cp->RTxC_base);
+		get_user(trxc, &cp->TRxC_base);
+		get_user(pclk, &cp->PCLK_base);
+
+		if (pclk == SCC_BAUD_BASE_NONE)
+			/* This is really not possible :-) */
+			return( -EINVAL );
+
+		/* Check the baud table for consistency */
+		for( i = 0; i < sizeof(cp->baud_table)/sizeof(cp->baud_table[0]); ++i ) {
+
+			get_user(clk, &cp->baud_table[i].clksrc);
+			get_user(div, &cp->baud_table[i].divisor);
+
+			switch( clk ) {
+			  case CLK_RTxC:
+				if (rtxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (((div & 1) && div != 1) ||
+				    (div >= 4 && div/2-2 > 65535))
+					return( -EINVAL );
+				break;
+			  case CLK_TRxC:
+				if (trxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (div != 1 && div != 2 && div != 4)
+					return( -EINVAL );
+				break;
+			  case CLK_PCLK:
+				if (div < 4 || (div & 1) || div/2-2 > 65535)
+					return( -EINVAL );
+				break;
+			  default:
+				/* invalid valid clock source */
+				return( -EINVAL );
+			}
+		}
+
+		/* After all the checks, set the values */
+
+		SCC_clocks[channel][CLK_RTxC] = rtxc;
+		SCC_clocks[channel][CLK_TRxC] = trxc;
+		SCC_PCLK = pclk;
+
+		copy_from_user(bdtab_usr[channel] + 1, cp->baud_table,
+			       sizeof(cp->baud_table));
+		/* Now use the user supplied baud table */
+		SCC_baud_table[channel] = bdtab_usr[channel];
+
+		return( 0 );
+
+	  case TIOCDATSCC:
+
+		if (!suser()) return( -EPERM );
+#ifdef ENABLE_ATARI_SCC
+		if (!MACH_IS_ATARI)
+			return 0; /* XXX */
+
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			SCC_clocks[channel][CLK_RTxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_PCLK4 :
+					SCC_BAUD_BASE_TIMC;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+		else {
+			SCC_clocks[channel][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+
+		SCC_PCLK = SCC_BAUD_BASE_PCLK;
+		SCC_baud_table[channel] =
+			((ATARIHW_PRESENT(TT_MFP) && channel == 1) ?
+			 bdtab_TTChB : bdtab_norm);
+#endif
+		return( 0 );
+
+	}
+	return( -ENOIOCTLCMD );
+}
+
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI)
+		return atari_SCC_init();
+#endif
+	return -ENODEV;
+}
+
+void cleanup_module(void)
+{
+	if (chb_line >= 0) {
+		SCC_deinit_port( &rs_table[chb_line], CHANNEL_B );
+		unregister_serial( chb_line );
+	}
+
+    /* ++Juergen Starek: use proper structure to deinitialize port
+     *                   because atari_free_irq relies on the valid
+     *                   `dev_id` parameter!
+     *			  If we use only the cha232_line, unloading a 
+     *			  module causes a damaged irq list!
+     */
+	/* We must deinit channel A only once! ++Andreas. */
+	if (cha232_line >= 0)
+		SCC_deinit_port(&rs_table[cha232_line], CHANNEL_A);
+	else if (cha422_line >= 0)
+		SCC_deinit_port(&rs_table[cha422_line], CHANNEL_A);
+
+	if (cha232_line >= 0)
+		unregister_serial( cha232_line );
+	if (cha422_line >= 0)
+		unregister_serial( cha422_line );
+}
+#endif
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN linux-2.2.18-pre15/drivers/char/atari_SCC.h linux/drivers/char/atari_SCC.h
--- linux-2.2.18-pre15/drivers/char/atari_SCC.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/atari_SCC.h	Mon Dec  3 20:34:06 2001
@@ -0,0 +1,616 @@
+/*
+ * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+#ifndef _ATARI_SCC_H
+#define _ATARI_SCC_H
+
+#include <linux/config.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_MVME147_SCC
+#include <asm/mvme147hw.h>
+#endif
+#ifdef CONFIG_MVME162_SCC
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef CONFIG_BVME6000_SCC
+#include <asm/bvme6000hw.h>
+#endif
+#ifdef CONFIG_ATARI
+#include <asm/atarihw.h>
+#endif
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Names                          */
+/*                                                                     */
+/***********************************************************************/
+
+/* The SCC documentation gives no explicit names to the registers,
+ * they're just called WR0..15 and RR0..15. To make the source code
+ * better readable and make the transparent write reg read access (see
+ * below) possible, I christen them here with self-invented names.
+ * Note that (real) read registers are assigned numbers 16..31. WR7'
+ * has number 33.
+ */
+
+#define	COMMAND_REG			0	/* wo */
+#define	INT_AND_DMA_REG		1	/* wo */
+#define	INT_VECTOR_REG		2	/* rw, common to both channels */
+#define	RX_CTRL_REG			3	/* rw */
+#define	AUX1_CTRL_REG		4	/* rw */
+#define	TX_CTRL_REG			5	/* rw */
+#define	SYNC_ADR_REG		6	/* wo */
+#define	SYNC_CHAR_REG		7	/* wo */
+#define	SDLC_OPTION_REG		33	/* wo */
+#define	TX_DATA_REG			8	/* wo */
+#define	MASTER_INT_CTRL		9	/* wo, common to both channels */
+#define	AUX2_CTRL_REG		10	/* rw */
+#define	CLK_CTRL_REG		11	/* wo */
+#define	TIMER_LOW_REG		12	/* rw */
+#define	TIMER_HIGH_REG		13	/* rw */
+#define	DPLL_CTRL_REG		14	/* wo */
+#define	INT_CTRL_REG		15	/* rw */
+
+#define	STATUS_REG			16	/* ro */
+#define	SPCOND_STATUS_REG	17	/* wo */
+/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */
+#define	CURR_VECTOR_REG		18	/* Ch. B only, Ch. A for rw */
+#define	INT_PENDING_REG		19	/* Channel A only! */
+/* RR4 is WR4, if b6(MR7') == 1 */
+/* RR5 is WR5, if b6(MR7') == 1 */
+#define	FS_FIFO_LOW_REG		22	/* ro */
+#define	FS_FIFO_HIGH_REG	23	/* ro */
+#define	RX_DATA_REG			24	/* ro */
+/* RR9 is WR3, if b6(MR7') == 1 */
+#define	DPLL_STATUS_REG		26	/* ro */
+/* RR11 is WR10, if b6(MR7') == 1 */
+/* RR12 is WR12 */
+/* RR13 is WR13 */
+/* RR14 not present */
+/* RR15 is WR15 */
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Values                         */
+/*                                                                     */
+/***********************************************************************/
+
+
+/* WR0: COMMAND_REG "CR" */
+
+#define	CR_RX_CRC_RESET			0x40
+#define	CR_TX_CRC_RESET			0x80
+#define	CR_TX_UNDERRUN_RESET	0xc0
+
+#define	CR_EXTSTAT_RESET		0x10
+#define	CR_SEND_ABORT			0x18
+#define	CR_ENAB_INT_NEXT_RX		0x20
+#define	CR_TX_PENDING_RESET		0x28
+#define	CR_ERROR_RESET			0x30
+#define	CR_HIGHEST_IUS_RESET	0x38
+
+
+/* WR1: INT_AND_DMA_REG "IDR" */
+
+#define	IDR_EXTSTAT_INT_ENAB	0x01
+#define	IDR_TX_INT_ENAB			0x02
+#define	IDR_PARERR_AS_SPCOND	0x04
+
+#define	IDR_RX_INT_DISAB		0x00
+#define	IDR_RX_INT_FIRST		0x08
+#define	IDR_RX_INT_ALL			0x10
+#define	IDR_RX_INT_SPCOND		0x18
+#define	IDR_RX_INT_MASK			0x18
+
+#define	IDR_WAITREQ_RX			0x20
+#define	IDR_WAITREQ_IS_REQ		0x40
+#define	IDR_WAITREQ_ENAB		0x80
+
+
+/* WR3: RX_CTRL_REG "RCR" */
+
+#define	RCR_RX_ENAB				0x01
+#define	RCR_DISCARD_SYNC_CHARS	0x02
+#define	RCR_ADDR_SEARCH			0x04
+#define	RCR_CRC_ENAB			0x08
+#define	RCR_SEARCH_MODE			0x10
+#define	RCR_AUTO_ENAB_MODE		0x20
+
+#define	RCR_CHSIZE_MASK			0xc0
+#define	RCR_CHSIZE_5			0x00
+#define	RCR_CHSIZE_6			0x40
+#define	RCR_CHSIZE_7			0x80
+#define	RCR_CHSIZE_8			0xc0
+
+
+/* WR4: AUX1_CTRL_REG "A1CR" */
+
+#define	A1CR_PARITY_MASK		0x03
+#define	A1CR_PARITY_NONE		0x00
+#define	A1CR_PARITY_ODD			0x01
+#define	A1CR_PARITY_EVEN		0x03
+
+#define	A1CR_MODE_MASK			0x0c
+#define	A1CR_MODE_SYNCR			0x00
+#define	A1CR_MODE_ASYNC_1		0x04
+#define	A1CR_MODE_ASYNC_15		0x08
+#define	A1CR_MODE_ASYNC_2		0x0c
+
+#define	A1CR_SYNCR_MODE_MASK	0x30
+#define	A1CR_SYNCR_MONOSYNC		0x00
+#define	A1CR_SYNCR_BISYNC		0x10
+#define	A1CR_SYNCR_SDLC			0x20
+#define	A1CR_SYNCR_EXTCSYNC		0x30
+
+#define	A1CR_CLKMODE_MASK		0xc0
+#define	A1CR_CLKMODE_x1			0x00
+#define	A1CR_CLKMODE_x16		0x40
+#define	A1CR_CLKMODE_x32		0x80
+#define	A1CR_CLKMODE_x64		0xc0
+
+
+/* WR5: TX_CTRL_REG "TCR" */
+
+#define	TCR_TX_CRC_ENAB			0x01
+#define	TCR_RTS					0x02
+#define	TCR_USE_CRC_CCITT		0x00
+#define	TCR_USE_CRC_16			0x04
+#define	TCR_TX_ENAB				0x08
+#define	TCR_SEND_BREAK			0x10
+
+#define	TCR_CHSIZE_MASK			0x60
+#define	TCR_CHSIZE_5			0x00
+#define	TCR_CHSIZE_6			0x20
+#define	TCR_CHSIZE_7			0x40
+#define	TCR_CHSIZE_8			0x60
+
+#define	TCR_DTR					0x80
+
+
+/* WR7': SLDC_OPTION_REG "SOR" */
+
+#define	SOR_AUTO_TX_ENAB		0x01
+#define	SOR_AUTO_EOM_RESET		0x02
+#define	SOR_AUTO_RTS_MODE		0x04
+#define	SOR_NRZI_DISAB_HIGH		0x08
+#define	SOR_ALT_DTRREQ_TIMING	0x10
+#define	SOR_READ_CRC_CHARS		0x20
+#define	SOR_EXTENDED_REG_ACCESS	0x40
+
+
+/* WR9: MASTER_INT_CTRL "MIC" */
+
+#define	MIC_VEC_INCL_STAT		0x01
+#define	MIC_NO_VECTOR			0x02
+#define	MIC_DISAB_LOWER_CHAIN	0x04
+#define	MIC_MASTER_INT_ENAB		0x08
+#define	MIC_STATUS_HIGH			0x10
+#define	MIC_IGN_INTACK			0x20
+
+#define	MIC_NO_RESET			0x00
+#define	MIC_CH_A_RESET			0x40
+#define	MIC_CH_B_RESET			0x80
+#define	MIC_HARD_RESET			0xc0
+
+
+/* WR10: AUX2_CTRL_REG "A2CR" */
+
+#define	A2CR_SYNC_6				0x01
+#define	A2CR_LOOP_MODE			0x02
+#define	A2CR_ABORT_ON_UNDERRUN	0x04
+#define	A2CR_MARK_IDLE			0x08
+#define	A2CR_GO_ACTIVE_ON_POLL	0x10
+
+#define	A2CR_CODING_MASK		0x60
+#define	A2CR_CODING_NRZ			0x00
+#define	A2CR_CODING_NRZI		0x20
+#define	A2CR_CODING_FM1			0x40
+#define	A2CR_CODING_FM0			0x60
+
+#define	A2CR_PRESET_CRC_1		0x80
+
+
+/* WR11: CLK_CTRL_REG "CCR" */
+
+#define	CCR_TRxCOUT_MASK		0x03
+#define	CCR_TRxCOUT_XTAL		0x00
+#define	CCR_TRxCOUT_TXCLK		0x01
+#define	CCR_TRxCOUT_BRG			0x02
+#define	CCR_TRxCOUT_DPLL		0x03
+
+#define	CCR_TRxC_OUTPUT			0x04
+
+#define	CCR_TXCLK_MASK			0x18
+#define	CCR_TXCLK_RTxC			0x00
+#define	CCR_TXCLK_TRxC			0x08
+#define	CCR_TXCLK_BRG			0x10
+#define	CCR_TXCLK_DPLL			0x18
+
+#define	CCR_RXCLK_MASK			0x60
+#define	CCR_RXCLK_RTxC			0x00
+#define	CCR_RXCLK_TRxC			0x20
+#define	CCR_RXCLK_BRG			0x40
+#define	CCR_RXCLK_DPLL			0x60
+
+#define	CCR_RTxC_XTAL			0x80
+
+
+/* WR14: DPLL_CTRL_REG "DCR" */
+
+#define	DCR_BRG_ENAB			0x01
+#define	DCR_BRG_USE_PCLK		0x02
+#define	DCR_DTRREQ_IS_REQ		0x04
+#define	DCR_AUTO_ECHO			0x08
+#define	DCR_LOCAL_LOOPBACK		0x10
+
+#define	DCR_DPLL_EDGE_SEARCH	0x20
+#define	DCR_DPLL_ERR_RESET		0x40
+#define	DCR_DPLL_DISAB			0x60
+#define	DCR_DPLL_CLK_BRG		0x80
+#define	DCR_DPLL_CLK_RTxC		0xa0
+#define	DCR_DPLL_FM				0xc0
+#define	DCR_DPLL_NRZI			0xe0
+
+
+/* WR15: INT_CTRL_REG "ICR" */
+
+#define	ICR_OPTIONREG_SELECT	0x01
+#define	ICR_ENAB_BRG_ZERO_INT	0x02
+#define	ICR_USE_FS_FIFO			0x04
+#define	ICR_ENAB_DCD_INT		0x08
+#define	ICR_ENAB_SYNC_INT		0x10
+#define	ICR_ENAB_CTS_INT		0x20
+#define	ICR_ENAB_UNDERRUN_INT	0x40
+#define	ICR_ENAB_BREAK_INT		0x80
+
+
+/* RR0: STATUS_REG "SR" */
+
+#define	SR_CHAR_AVAIL			0x01
+#define	SR_BRG_ZERO				0x02
+#define	SR_TX_BUF_EMPTY			0x04
+#define	SR_DCD					0x08
+#define	SR_SYNC_ABORT			0x10
+#define	SR_CTS					0x20
+#define	SR_TX_UNDERRUN			0x40
+#define	SR_BREAK				0x80
+
+
+/* RR1: SPCOND_STATUS_REG "SCSR" */
+
+#define	SCSR_ALL_SENT			0x01
+#define	SCSR_RESIDUAL_MASK		0x0e
+#define	SCSR_PARITY_ERR			0x10
+#define	SCSR_RX_OVERRUN			0x20
+#define	SCSR_CRC_FRAME_ERR		0x40
+#define	SCSR_END_OF_FRAME		0x80
+
+
+/* RR3: INT_PENDING_REG "IPR" */
+
+#define	IPR_B_EXTSTAT			0x01
+#define	IPR_B_TX				0x02
+#define	IPR_B_RX				0x04
+#define	IPR_A_EXTSTAT			0x08
+#define	IPR_A_TX				0x10
+#define	IPR_A_RX				0x20
+
+
+/* RR7: FS_FIFO_HIGH_REG "FFHR" */
+
+#define	FFHR_CNT_MASK			0x3f
+#define	FFHR_IS_FROM_FIFO		0x40
+#define	FFHR_FIFO_OVERRUN		0x80
+
+
+/* RR10: DPLL_STATUS_REG "DSR" */
+
+#define	DSR_ON_LOOP				0x02
+#define	DSR_ON_LOOP_SENDING		0x10
+#define	DSR_TWO_CLK_MISSING		0x40
+#define	DSR_ONE_CLK_MISSING		0x80
+
+
+
+/***************************** Prototypes *****************************/
+
+int atari_SCC_init( void );
+
+/************************* End of Prototypes **************************/
+
+/* Compute the channel number from the base address */
+
+#define CHANNR(info)	(((info)->port & (MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) != ChannelsReversed)
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                               Constants                             */
+/*                                                                     */
+/***********************************************************************/
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Access                         */
+/*                                                                     */
+/***********************************************************************/
+
+
+/* The SCC needs 3.5 PCLK cycles recovery time between to register
+ * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 *
+ * 125 ns = 437.5 ns. Since this is too short for udelay(), it is
+ * implemented by some nop's. I think that a nop needs 4 cycles (but
+ * I'm not sure, correct me please!), that gives 4 nops for a TT (32
+ * MHz) (2 would be sufficient for the Falcon (16 MHz), but looking at
+ * boot_info.bi_atari.model at runtime takes longer than 2 nop's...)
+ * ++andreas: nop needs only 2 cycles, seven of them are needed.
+ */
+
+/* 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be
+ * quite right
+ */
+
+#define scc_reg_delay() \
+    do {			\
+	if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)	\
+		udelay(1);	\
+	else			\
+		__asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\
+    } while (0)
+
+/* Another version with only 3 nop's for cases when some other
+ * statement intervenes between the two SCC accesses
+ * ++andreas: 3 nop's added
+ * 10/16/95: use MFPDELAY, too.
+ */
+
+#define scc_reg3_delay() \
+	if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)	\
+		udelay(1);	\
+	else			\
+		__asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );
+
+
+struct PARTIAL_SCC {		/* just one channel */
+	unsigned char	ctrl;
+	unsigned char	dummy;
+	unsigned char	data;
+};
+
+
+extern unsigned char SCC_shadow[2][16];
+extern int ChannelsReversed;
+
+
+/* The following functions should relax the somehow complicated
+ * register access of the SCC. _SCCwrite() stores all written values
+ * (except for WR0 and WR8) in shadow registers for later recall. This
+ * removes the burden of remembering written values as needed. The
+ * extra work of storing the value doesn't count, since a delay is
+ * needed after a SCC access anyway. Additionally, _SCCwrite() manages
+ * writes to WR0 and WR8 differently, because these can be accessed
+ * directly with less overhead. Another special case are WR7 and WR7'.
+ * _SCCwrite automatically checks what of this registers is selected
+ * and changes b0 of WR15 if needed.
+ * 
+ * _SCCread() for standard read registers is straightforward, except
+ * for RR2 (split into two "virtual" registers: one for the value
+ * written to WR2 (from the shadow) and one for the vector including
+ * status from RR2, Ch. B) and RR3. The latter must be read from
+ * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can
+ * be accessed directly as before.
+ * 
+ * The two inline function contain complicated switch statements. But
+ * I rely on regno and final_delay being constants, so gcc can reduce
+ * the whole stuff to just some assembler statements.
+ * 
+ * _SCCwrite and _SCCread aren't intended to be used directly under
+ * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are
+ * for that purpose. They assume that a local variable 'info' is
+ * declared and pointing to the port's m68k_async_struct entry. The
+ * variants with "_NB" appended should be used if no other SCC
+ * accesses follow immediatly (within 0.5 usecs). They just skip the
+ * final delay nops.
+ * 
+ * Please note that accesses to SCC registers should only take place
+ * when interrupts are turned off (at least if SCC interrupts are
+ * enabled). Otherwise, an interrupt could interfere with the
+ * two-stage accessing process.
+ *
+ */
+
+
+static __inline__ void _SCCwrite(
+	volatile struct PARTIAL_SCC *sc,
+	unsigned char *shadow,
+	volatile unsigned char *_scc_del,
+	int regno,
+	unsigned char val, int final_delay )
+{
+	switch( regno ) {
+
+	  case COMMAND_REG:
+		/* WR0 can be written directly without pointing */
+		sc->ctrl = val;
+		break;
+
+	  case SYNC_CHAR_REG:
+		/* For WR7, first set b0 of WR15 to 0, if needed */
+		if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) {
+			sc->ctrl = 15;
+			shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT;
+			scc_reg3_delay();
+			sc->ctrl = shadow[INT_CTRL_REG];
+			scc_reg_delay();
+		}
+		goto normal_case;
+		
+	  case SDLC_OPTION_REG:
+		/* For WR7', first set b0 of WR15 to 1, if needed */
+		if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) {
+			sc->ctrl = 15;
+			shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT;
+			scc_reg3_delay();
+			sc->ctrl = shadow[INT_CTRL_REG];
+			scc_reg_delay();
+		}
+		sc->ctrl = 7;
+		shadow[8] = val;	/* WR7' shadowed at WR8 */
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case TX_DATA_REG:		/* WR8 */
+		/* TX_DATA_REG can be accessed directly on some h/w */
+		if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
+		{
+			sc->ctrl = regno;
+			scc_reg_delay();
+			sc->ctrl = val;
+		}
+		else
+			sc->data = val;
+		break;
+
+	  case MASTER_INT_CTRL:
+		sc->ctrl = regno;
+		val &= 0x3f;	/* bits 6..7 are the reset commands */
+		SCC_shadow[0][regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case DPLL_CTRL_REG:
+		sc->ctrl = regno;
+		val &= 0x1f;			/* bits 5..7 are the DPLL commands */
+		shadow[regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case 1 ... 6:	
+	  case 10 ... 13:
+	  case 15:
+	  normal_case:
+		sc->ctrl = regno;
+		shadow[regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+		
+	  default:
+		printk( "Bad SCC write access to WR%d\n", regno );
+		break;
+		
+	}
+
+	if (final_delay)
+		scc_reg_delay();
+}
+
+
+static __inline__ unsigned char _SCCread(
+	volatile struct PARTIAL_SCC *sc,
+	unsigned char *shadow,
+	volatile unsigned char *_scc_del,
+	int regno, int final_delay )
+{
+	unsigned char rv;
+
+	switch( regno ) {
+
+		/* --- real read registers --- */
+	  case STATUS_REG:
+		rv = sc->ctrl;
+		break;
+
+	  case INT_PENDING_REG:
+		/* RR3: read only from Channel A! */
+		sc = (volatile struct PARTIAL_SCC *)
+			(((unsigned long)sc & ~(MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) ^ ChannelsReversed);
+		goto normal_case;
+
+	  case RX_DATA_REG:
+		/* RR8 can be accessed directly on some h/w */
+		if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
+		{
+			sc->ctrl = 8;
+			scc_reg_delay();
+			rv = sc->ctrl;
+		}
+		else
+			rv = sc->data;
+		break;
+
+	  case CURR_VECTOR_REG:
+		/* RR2 (vector including status) from Ch. B */
+		sc = (volatile struct PARTIAL_SCC *)
+			(((unsigned long)sc | (MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) ^ ChannelsReversed);
+		goto normal_case;
+		
+		/* --- reading write registers: access the shadow --- */
+	  case 1 ... 7:
+	  case 10 ... 15:
+		return shadow[regno]; /* no final delay! */
+
+		/* WR7' is special, because it is shadowed at the place of WR8 */
+	  case SDLC_OPTION_REG:
+		return shadow[8]; /* no final delay! */
+
+		/* WR9 is special too, because it is common for both channels */
+	  case MASTER_INT_CTRL:
+		return SCC_shadow[0][9]; /* no final delay! */
+
+	  default:
+		printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W',
+				regno & ~16 );
+		break;
+		
+	  case SPCOND_STATUS_REG:
+	  case FS_FIFO_LOW_REG:
+	  case FS_FIFO_HIGH_REG:
+	  case DPLL_STATUS_REG:
+	  normal_case:
+		sc->ctrl = regno & 0x0f;
+		scc_reg_delay();
+		rv = sc->ctrl;
+		break;
+		
+	}
+
+	if (final_delay)
+		scc_reg_delay();
+	return rv;
+}
+
+/*
+ * The BVME6000 maps the two halves of the SCC 8 bytes apart rather than 4.
+ * The MVME147  maps the two halves of the SCC 2 bytes apart rather than 4.
+ */
+
+#define SCC_ACCESS_INIT(info)						\
+	volatile struct PARTIAL_SCC *_SCC_p =				\
+		(volatile struct PARTIAL_SCC *)info->port;		\
+	unsigned char *_SCC_shadow = &SCC_shadow[(info->port >>		\
+		(MACH_IS_BVME6000 ? 3 : (MACH_IS_MVME147 ? 1 : 2))) & 1][0]
+
+#define	SCCwrite(reg,val)		_SCCwrite(_SCC_p,_SCC_shadow,scc_del,(reg),(val),1)
+#define	SCCwrite_NB(reg,val)	_SCCwrite(_SCC_p,_SCC_shadow,scc_del,(reg),(val),0)
+#define	SCCread(reg)			_SCCread(_SCC_p,_SCC_shadow,scc_del,(reg),1)
+#define	SCCread_NB(reg)			_SCCread(_SCC_p,_SCC_shadow,scc_del,(reg),0)
+
+#define SCCmod(reg,and,or)		SCCwrite((reg),(SCCread(reg)&(and))|(or))
+
+#endif /* _ATARI_SCC_H */
diff -urN linux-2.2.18-pre15/drivers/char/lp_mfc.c linux/drivers/char/lp_mfc.c
--- linux-2.2.18-pre15/drivers/char/lp_mfc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/lp_mfc.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,174 @@
+/*
+ * lp driver for the parallel port of a Multiface Card III
+ * 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ * ported to 2.0.18 and modularised
+ * 25.9.96 Joerg Dorchain
+ *
+ * ported to 2.1.42
+ * 24.6.97 Joerg Dorchain (jdorchain@i-con.de)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/lp_m68k.h>
+#include <linux/lp_mfc.h>
+#include <linux/mc6821.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include "multiface.h"
+
+static void lp_mfc_out(int,int);
+static int lp_mfc_busy(int);
+static int lp_mfc_pout(int);
+static int lp_mfc_online(int);
+static void lp_mfc_interrupt(int, void *, struct pt_regs *);
+
+static inline struct pia *pia(int);
+
+static volatile int dummy; /* for trigger reads */
+static int minor[MAX_LP] = { -1, -1, -1, -1, -1 };
+MODULE_PARM(minor,"1-" __MODULE_STRING(MAX_LP) "i");
+#ifdef MODULE
+static unsigned int board_key[MAX_LP];
+#endif
+
+static void lp_mfc_out(int c, int dev)
+{
+int wait = 0;
+
+while (wait != lp_table[dev]->wait) wait++;
+dummy = pia(dev)->pprb; /* trigger read clears irq bit*/
+pia(dev)->pprb = c;  /* strobe goes down by hardware */
+}
+
+static int lp_mfc_busy(int dev)
+{
+return pia(dev)->ppra&1;
+}
+
+static int lp_mfc_pout(int dev)
+{
+return pia(dev)->ppra&2;
+}
+
+static int lp_mfc_online(int dev)
+{
+return pia(dev)->ppra&4;
+}
+
+static void lp_mfc_interrupt(int irq,void *data,struct pt_regs *fp)
+{
+int i;
+
+for( i=0; i<MAX_LP; i++)  /* test all lp's */
+	if (minor[i] != -1)  /* one of ours ? */
+		if (pia(minor[i])->crb&128) { /* has an irq? */
+			dummy = pia(minor[i])->pprb; /* clear bit */
+			lp_interrupt(minor[i]);
+		}
+}
+
+static inline struct pia *pia(int dev)
+{
+return lp_table[dev]->base;
+}
+
+static int lp_mfc_open(int dev)
+{
+MOD_INC_USE_COUNT;
+return 0;
+}
+
+static void lp_mfc_release(int dev)
+{
+MOD_DEC_USE_COUNT;
+}
+
+static struct lp_struct tab[MAX_LP] = {{0,},};
+
+__initfunc(int lp_mfc_init(void))
+{
+int pias;
+struct pia *pp;
+unsigned int key = 0;
+const struct ConfigDev *cd;
+
+if (!MACH_IS_AMIGA)
+  return -ENODEV;
+
+pias = 0;
+while((key = zorro_find(ZORRO_PROD_BSC_MULTIFACE_III, 0 , key))) {
+  cd = zorro_get_board( key );
+  pp = (struct pia *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+PIABASE));
+  if (pias < MAX_LP) {
+	pp->crb = 0;
+	pp->pddrb = 255;    /* all pins output */
+	pp->crb = PIA_DDR|32|8;
+	dummy = pp->pprb;
+	pp->crb |=(lp_irq!=0)?PIA_C1_ENABLE_IRQ:0; 
+	pp->cra = 0;
+	pp->pddra = 0xe0;   /* /RESET,  /DIR ,/AUTO-FEED output */
+	pp->cra = PIA_DDR;
+	pp->ppra = 0;    /* reset printer */
+	udelay(5);
+	pp->ppra |= 128;
+	tab[pias].name="Multiface III LP";
+	tab[pias].lp_out=lp_mfc_out;
+	tab[pias].lp_is_busy=lp_mfc_busy;
+	tab[pias].lp_has_pout=lp_mfc_pout;
+	tab[pias].lp_is_online=lp_mfc_online;
+	tab[pias].lp_ioctl=NULL;
+	tab[pias].lp_open=lp_mfc_open;
+	tab[pias].lp_release=lp_mfc_release;
+	tab[pias].flags=LP_EXIST;
+	tab[pias].chars=LP_INIT_CHAR;
+	tab[pias].time=LP_INIT_TIME;
+	tab[pias].wait=LP_INIT_WAIT;
+	tab[pias].lp_wait_q=NULL;
+	tab[pias].base=pp;
+	tab[pias].type=LP_MFC;
+	if ((minor[pias] = register_parallel(tab + pias, minor[pias] )) >= 0) {
+	  zorro_config_board( key, 0 );
+#ifdef MODULE
+	  board_key[minor[pias]] = key;
+#endif
+	  pias++;
+	}
+	else
+	  printk("mfc_init: cant get a minor for pia at 0x%08lx\n",(long)pp);
+  }
+}
+if ((pias != 0) && (lp_irq != 0))
+  request_irq(IRQ_AMIGA_PORTS, lp_mfc_interrupt, 0,
+    "Multiface III printer", lp_mfc_interrupt);
+
+return (pias==0)?-ENODEV:0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return lp_mfc_init();
+}
+
+void cleanup_module(void)
+{
+int i;
+
+if (lp_irq)
+  free_irq(IRQ_AMIGA_PORTS, lp_mfc_interrupt);
+for(i = 0; i < MAX_LP; i++)
+  if ((lp_table[i] != NULL) && (lp_table[i]->type == LP_MFC)) {
+    unregister_parallel(i);
+    zorro_unconfig_board(board_key[i], 0);
+  }
+}
+#endif
diff -urN linux-2.2.18-pre15/drivers/char/m68kserial.c linux/drivers/char/m68kserial.c
--- linux-2.2.18-pre15/drivers/char/m68kserial.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/m68kserial.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,1861 @@
+/*
+ *  linux/drivers/char/m68kserial.c
+ *
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
+ *  much more extensible to support other serial cards based on the
+ *  16450/16550A UART's.  Added support for the AST FourPort and the
+ *  Accent Async board.  
+ *
+ *  set_serial_info fixed to set the flags, custom divisor, and uart
+ * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ *	long m68k_rs_init(void);
+ */
+
+/*
+ * Notes and Design Goals:
+ * -----------------------
+ * The PC serial drivers can rely on the fact that all the serial
+ * hardware is very similar to program for all ports. Unfortunately,
+ * this is not true for m68k machines, especially the Atari. Here it is
+ * nearly the other way 'round: All ports need different treatment for
+ * the low-level stuff.
+ *
+ * For this reason, I've split the serial driver code into a
+ * port-independent part (serial.c) and port-specific parts (atari_*.c,
+ * ...). The first manages all what can be done without accessing the
+ * hardware directly, i.e. interfacing with the high-level tty drivers,
+ * wait queues, managing existing ports and the like. The latter do the
+ * actual hardware programming and are accessed by the hardware
+ * independent part by a "switch" structure, that contains pointers to
+ * functions for specific tasks. See the comment before the definition
+ * of the SERIALSWITCH structure in <linux/serial.h> for more details.
+ *
+ * The port-independent code should be usable by other machines than
+ * m68k ones, too, if there are similar circumstances with different
+ * serial port hardware. Feel free to use it, but please inform me if
+ * you have to do changes to it. I'll try to keep it really
+ * device-independent.
+ *
+ * Roman <Roman.Hodek@informatik.uni-erlangen.de>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/m68kserial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#ifdef CONFIG_ATARI
+#include "atari_SCC.h"
+#include "atari_MFPser.h"
+#include "atari_MIDI.h"
+#endif
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifdef CONFIG_MAC_SCC
+int mac_SCC_init(void);
+#endif
+
+#ifdef CONFIG_AMIGA_BUILTIN_SERIAL
+int amiga_serinit (void);
+#endif
+
+#ifdef CONFIG_GVPIOEXT
+int ioext_init (void);
+#endif
+
+#ifdef CONFIG_MULTIFACE_III_TTY
+int multiface_init(void);
+#endif
+
+#ifdef CONFIG_MVME147_SCC
+int m147_SCC_init(void);
+#endif
+
+#ifdef CONFIG_MVME162_SCC
+int mvme_SCC_init(void);
+#endif
+
+#ifdef CONFIG_BVME6000_SCC
+int bvme_SCC_init(void);
+#endif
+
+#ifdef CONFIG_WHIPPET
+int whippet_init (void);
+#endif
+
+#ifdef CONFIG_HYPERCOM1
+int hypercom1_init(void);
+#endif
+
+#ifdef CONFIG_HPDCA
+int hpdca_init(void);
+#endif
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define _INLINE_ inline
+
+#define NR_PORTS	6
+
+struct m68k_async_struct rs_table[NR_PORTS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console sercons;
+#endif
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static char *serialtypes[] = {
+	"unknown", "8250", "16450", "16550", "16550A"
+};
+#define PORT_MAX (sizeof(serialtypes)/sizeof(*serialtypes))
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+static char *serialtypes68k[] = {
+	"SCC w/o DMA", "SCC w/ DMA",
+	"MFP", "MFP w/o ctrl lines",
+	"MIDI",
+	"Amiga builtin", "GVP IO-Extender (16c552)", "BSC MultiFaceCard III",
+	"Hisoft Whippet",
+	"SCC on MVME",
+	"8350 ESCC w/o DMA",
+	"HP DCA",
+	"SCC on BVME", "A1200 HyperCOM1"
+};
+#define M68K_PORT_MAX (sizeof(serialtypes68k)/sizeof(*serialtypes68k))
+#endif
+
+#ifdef CONFIG_PROC_FS
+static int rs_read_proc (char *buffer, char **start, off_t offset, int size,
+			 int *eof, void *data)
+{
+    int len, i;
+    off_t begin = 0;
+    char *name;
+
+    len = sprintf (buffer, "Serial ports:\n");
+    for (i = 0; i < NR_PORTS; ++i) {
+	struct m68k_async_struct *info = &rs_table[i];
+
+	if (!info->port) continue;
+	if (info->type >= 0 && info->type < PORT_MAX)
+	    name = serialtypes[info->type];
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+	else if (info->type >= 100 && info->type < 100 + M68K_PORT_MAX)
+	    name = serialtypes68k[info->type - 100];
+#endif
+	else
+	    name = "unknown";
+	len += sprintf (buffer + len, "%d: name:%s port:0x%08x tx:%d rx:%d",
+			i, name, info->port, info->icount.tx, info->icount.rx);
+
+	if (info->icount.frame)
+		len += sprintf(buffer + len, " fe:%d", info->icount.frame);
+	if (info->icount.parity)
+		len += sprintf(buffer + len, " pe:%d", info->icount.parity);
+	if (info->icount.brk)
+		len += sprintf(buffer + len, " brk:%d", info->icount.brk);
+	if (info->icount.overrun)
+		len += sprintf(buffer + len, " oe:%d", info->icount.overrun);
+	len += sprintf(buffer + len, "\n");
+
+	if (len + begin > offset + size)
+	    goto done;
+	if (len + begin < offset) {
+	    begin += len;
+	    len = 0;
+	}
+    }
+    *eof = 1;
+done:
+    if (offset >= len + begin)
+	return 0;
+    *start = buffer + (begin - offset);
+    return (size < begin + len - offset ? size : begin + len - offset);
+}
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct m68k_async_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	const char *badinfo =
+		"Warning: null m68k_async_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+void rs_stop(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+
+	info->sw->enab_tx_int( info, 0 );
+}
+
+void rs_start(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
+
+	info->sw->enab_tx_int( info, 1 );
+}
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct m68k_async_struct	*info = (struct m68k_async_struct *) private_;
+	struct tty_struct	*tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct m68k_async_struct * info)
+{
+	unsigned long flags;
+	unsigned long page;
+
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		restore_flags(flags);
+		return 0;
+	}
+
+	if (!info->port || !info->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		restore_flags(flags);
+		return 0;
+	}
+	if (info->xmit_buf)
+		free_page(page);
+	else
+		info->xmit_buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d...", info->line);
+#endif
+
+	/* initialize the hardware specific stuff for the port */
+	info->sw->init(info);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * Set the speed and other port parameters.
+	 */
+	info->sw->change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct m68k_async_struct * info)
+{
+	unsigned long	flags;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d\n", info->line);
+#endif
+
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+	wake_up_interruptible(&info->delta_msr_wait);
+
+	/* do hardware specific deinitialization for the port */
+	info->sw->deinit( info, info->tty &&
+			 !(info->tty->termios->c_cflag & HUPCL) );
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+		return;
+
+	if (!tty || !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= SERIAL_XMIT_SIZE-1;
+	info->xmit_cnt++;
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	info->sw->enab_tx_int( info, 1 );
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		return 0;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+	save_flags(flags);
+	while (1) {
+		cli();
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			restore_flags(flags);
+			copy_from_user(tmp_buf, buf, c);
+			cli();
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+		info->sw->enab_tx_int( info, 1 );
+	restore_flags(flags);
+	return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+	int	ret;
+
+	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+		return;
+	save_flags(flags);
+	cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	restore_flags(flags);
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		info->x_char = STOP_CHAR(tty);
+		/* make sure the XOFF char is sent out if the TX was idle */
+		info->sw->enab_tx_int( info, 1 );
+	}
+
+	if (C_CRTSCTS(tty))
+		info->sw->throttle( info, 1 );
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	unsigned long flags;
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		/* Protect against an tx int between the test and resetting x_char.
+		 * This would cause the XON to be lost. */
+		save_flags(flags);
+		cli();
+		if (info->x_char) {
+			info->x_char = 0;
+			restore_flags(flags);
+		}
+		else {
+			restore_flags(flags);
+			info->x_char = START_CHAR(tty);
+			/* make sure the XOFF char is sent out if the TX was idle */
+			info->sw->enab_tx_int( info, 1 );
+		}
+	}
+
+	if (C_CRTSCTS(tty))
+		info->sw->throttle( info, 0 );
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct m68k_async_struct * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = 0;	/* meaningless for non-Intel */
+	tmp.irq  = 0;	/* meaningless for non-Intel */
+	tmp.flags = info->flags;
+	/* tmp.baud_base = info->baud_base;*/
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	tmp.hub6 = 0;	/* meaningless for non-Intel */
+
+	/* At least baud_base and costum_divisor set by port-specific
+	 * function
+	 */
+	info->sw->get_serial_info( info, &tmp );
+
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct m68k_async_struct * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct m68k_async_struct old_info;
+	int retval = 0;
+
+	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+		return -EFAULT;
+	old_info = *info;
+
+  	/* If a new custom divisor is to be set, let it check by the hardware
+ 	 * specific code first! (It is given the new info struct in case the
+ 	 * baud_base has also changed and valid divisors depend on the baud_base.)
+	 */
+	if (new_serial.custom_divisor != info->custom_divisor ||
+ 		new_serial.baud_base != info->baud_base) {
+		if (info->sw->check_custom_divisor( info, new_serial.baud_base,
+						   new_serial.custom_divisor ))
+			return( -EINVAL );
+	}
+
+	if (((new_serial.type != info->type) ||
+	     (new_serial.close_delay != info->close_delay) ||
+	     ((new_serial.flags & ~ASYNC_USR_MASK) !=
+	      (info->flags & ~ASYNC_USR_MASK)))
+	    && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+			(new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay * HZ/100;
+	info->closing_wait = new_serial.closing_wait * HZ/100;
+
+	if (!info->port || !info->type)
+		return 0;
+	if (info->flags & ASYNC_INITIALIZED) {
+		if (((old_info.flags & ASYNC_SPD_MASK) !=
+		     (info->flags & ASYNC_SPD_MASK)) ||
+		    (old_info.custom_divisor != info->custom_divisor) ||
+		    (old_info.baud_base != info->baud_base))
+			info->sw->change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+
+static int get_modem_info(struct m68k_async_struct * info, unsigned int *value)
+{
+	unsigned int result;
+
+	result = info->sw->get_modem_info (info);
+	return put_user(result,value);
+}
+
+static int set_modem_info(struct m68k_async_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	unsigned int arg;
+	int new_dtr = -1, new_rts = -1;
+
+	if (get_user(arg, value))
+		return -EFAULT;
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS)
+			new_rts = 1;
+		if (arg & TIOCM_DTR)
+			new_dtr = 1;
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			new_rts = 0;
+		if (arg & TIOCM_DTR)
+			new_dtr = 0;
+		break;
+	case TIOCMSET:
+		new_dtr = !!(arg & TIOCM_DTR);
+		new_rts = !!(arg & TIOCM_RTS);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return( info->sw->set_modem_info( info, new_dtr, new_rts ) );
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+	struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_break"))
+		return;
+
+	if (!info->port)
+		return;
+	save_flags(flags);
+	cli();
+	if (break_state == -1)
+		info->sw->set_break(info, 1);
+	else
+		info->sw->set_break(info, 0);
+	restore_flags(flags);
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data;
+	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERCONFIG:
+			/* return do_autoconfig(info); */
+			return ( 0 );
+
+		case TIOCSERGETLSR: /* Get line status register */
+			return put_user(0, (unsigned int *) arg);
+
+		case TIOCSERGSTRUCT:
+			if (copy_to_user((struct m68k_async_struct *) arg,
+					 info, sizeof(struct m68k_async_struct)))
+				return -EFAULT;
+			return 0;
+
+		case TIOCSERGETMULTI:
+		case TIOCSERSETMULTI:
+			return -EINVAL;
+
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+ 		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		 case TIOCMIWAIT:
+			save_flags(flags);
+			cli();
+			cprev = info->icount;	/* note the counters on entry */
+			restore_flags(flags);
+			while (1) {
+				interruptible_sleep_on(&info->delta_msr_wait);
+				/* see if a signal did it */
+				if (signal_pending(current))
+					return -ERESTARTSYS;
+				save_flags(flags);
+				cli();
+				cnow = info->icount;	/* atomic copy */
+				restore_flags(flags);
+				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+					return -EIO; /* no change => error */
+				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+					return 0;
+				}
+				cprev = cnow;
+			}
+			/* NOTREACHED */
+
+		/* 
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			save_flags(flags);
+			cli();
+			cnow = info->icount;
+			restore_flags(flags);
+			p_cuser = (struct serial_icounter_struct *) arg;
+			if (put_user(cnow.cts, &p_cuser->cts) ||
+			    put_user(cnow.dsr, &p_cuser->dsr) ||
+			    put_user(cnow.rng, &p_cuser->rng) ||
+			    put_user(cnow.dcd, &p_cuser->dcd))
+				return -EFAULT;
+			return 0;
+
+		default:
+			if (info->sw->ioctl)
+				return( info->sw->ioctl( tty, file, info, cmd, arg ) );
+			else
+				return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+
+	info->sw->change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+
+#if 0
+	/*
+	 * No need to wake up processes in open wait, since they
+	 * sample the CLOCAL flag once, and don't recheck it.
+	 * XXX  It's not clear whether the current behavior is correct
+	 * or not.  Hence, this may change.....
+	 */
+	if (!(old_termios->c_cflag & CLOCAL) &&
+	    (tty->termios->c_cflag & CLOCAL))
+		wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data;
+	unsigned long flags;
+	unsigned long timeout;
+
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+		return;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	if (info->flags & ASYNC_INITIALIZED) {
+		info->sw->stop_receive(info);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while (!(info->sw->trans_empty(info))) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(info->timeout);
+			if (signal_pending(current))
+				break;
+			if (time_after(jiffies, timeout))
+				break;
+		}
+		current->state = TASK_RUNNING;
+	}
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(info->close_delay);
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
+	restore_flags(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+	struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+		return;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct m68k_async_struct *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0;
+	unsigned long flags;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	save_flags(flags);
+	cli();
+	if (!tty_hung_up_p(filp))
+		info->count--;
+	restore_flags(flags);
+	info->blocked_open++;
+	while (1) {
+		save_flags(flags);
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+			info->sw->set_modem_info( info, 1, 1 );
+		restore_flags(flags);
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ASYNC_CLOSING) &&
+		    (do_clocal || (info->sw->get_modem_info( info ) & TIOCM_CAR)))
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct m68k_async_struct	*info;
+	int 			retval, line;
+	unsigned long		page;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENXIO;
+	info = rs_table + line;
+#ifdef CONFIG_KMOD
+	if (!info->port) {
+		char modname[30];
+		sprintf(modname, "char-major-%d-%d", TTY_MAJOR, MINOR(tty->device));
+		request_module(modname);
+	}
+#endif
+	if (!info->port)
+		return -ENXIO;
+	if (serial_paranoia_check(info, tty->device, "rs_open"))
+		return -ENODEV;
+
+	if (info->sw->check_open) {
+		if ((retval = info->sw->check_open( info, tty, filp )))
+			return( retval );
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	MOD_INC_USE_COUNT;
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("rs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else
+			*tty->termios = info->callout_termios;
+		info->sw->change_speed(info);
+	}
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		info->sw->change_speed(info);
+	}
+#endif
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * Boot-time initialization code: Init the serial ports present on
+ * this machine type.
+ *
+ * Current HW Port to minor/device name mapping:
+ *
+ * Atari
+ *   TT                    | Falcon                    || minor | name
+ *  -----------------------+---------------------------++-------+-------
+ *   ST-MFP port (Modem1)  | (Modem1, internal only)   || 64    | ttyS0
+ *   SCC B (Modem2)        | SCC B (Modem2)            || 65    | ttyS1
+ *   TT-MFP port (Serial1) |                           || 66    | ttyS2
+ *   SCC A (Serial2)       |                           || 67    | ttyS3
+ *   SCC A (LAN)           | SCC A (LAN)               || 68    | ttyS4
+ *   Atari MIDI            | Atari MIDI                || 69    | ttyS5
+ *
+ * New 22/12/94: Midi is implemented as a serial device to make use of
+ * MIDI networks easier (SLIP on ttyS5). If there is need for MIDI
+ * specific high-level code, it can be implemented as a line
+ * discipline in future. ttyS4 is reserved to make space for the LAN
+ * device. It seems to be the better solution to split Serial2 and LAN
+ * into separate minors. And maybe the port to device mapping will
+ * change in future...
+ *
+ * 02/08/95: Now the mapping has changed! My old mapping scheme of the serial
+ * ports was a flaw in respect to order the devices following their
+ * capabilities. And it confused the users, to... The "new" order is the same
+ * as the one used by TOS. The only difference is that Serial2/LAN is split
+ * into two devices, only one of which can be open at one time. If one of
+ * these devices is opened, the respective port's hardware is activated via
+ * IO7 of the PSG (if connected...)
+ *
+ * Amiga
+ *   Amiga built-in serial port                        || 64    | ttyS0
+ *
+ * New 13/07/95: Support for multiple serial-boards of the same kind,
+ * and boards with dual (or higher) uarts. This is done by letting the
+ * drivers 'allocate' tty's in the rs_table[rs_count] array. A counter
+ * (rs_count) is therefore passed to all serial-init routines, that
+ * adds the amount of uarts detected, and returns the new value.
+ * I think this is the best way to manage the various serial-boards
+ * available (atleast for the Amiga), and should cause no harm to the
+ * old drivers.                         - Jes Sorensen (jds@kom.auc.dk)
+ *
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void show_serial_version(void)
+{
+	printk(KERN_INFO "M68K Serial driver version 1.01\n");
+}
+
+EXPORT_SYMBOL(rs_table);
+EXPORT_SYMBOL(tq_serial);
+EXPORT_SYMBOL(m68k_register_serial);
+EXPORT_SYMBOL(m68k_unregister_serial);
+EXPORT_SYMBOL(rs_start);
+EXPORT_SYMBOL(rs_stop);
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int __init m68k_rs_init(void)
+{
+	int i, rs_count;
+	struct m68k_async_struct * info;
+
+#ifdef CONFIG_Q40
+	if (MACH_IS_Q40)
+	  return 0;
+#endif	
+	rs_count = 0; /* All machines start at rs_table[0] (minor 64). */
+
+	init_bh(SERIAL_BH, do_serial_bh);
+
+#ifdef CONFIG_SERIAL_CONSOLE
+	/*
+	 *	The interrupt of the serial console port
+	 *	can't be shared.
+	 */
+	if (sercons.flags & CON_CONSDEV) {
+		for(i = 0; i < NR_PORTS; i++)
+			if (i != sercons.index &&
+			    rs_table[i].irq == rs_table[sercons.index].irq)
+				rs_table[i].irq = 0;
+	}
+#endif
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "serial";
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NR_PORTS;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = rs_open;
+	serial_driver.close = rs_close;
+	serial_driver.write = rs_write;
+	serial_driver.put_char = rs_put_char;
+	serial_driver.flush_chars = rs_flush_chars;
+	serial_driver.write_room = rs_write_room;
+	serial_driver.chars_in_buffer = rs_chars_in_buffer;
+	serial_driver.flush_buffer = rs_flush_buffer;
+	serial_driver.ioctl = rs_ioctl;
+	serial_driver.throttle = rs_throttle;
+	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.set_termios = rs_set_termios;
+	serial_driver.stop = rs_stop;
+	serial_driver.start = rs_start;
+	serial_driver.hangup = rs_hangup;
+	serial_driver.break_ctl = rs_break;
+ 	/* TODO: serial_driver.wait_until_sent = rs_wait_until_sent; */
+#ifdef CONFIG_PROC_FS
+	serial_driver.read_proc = rs_read_proc;
+#endif
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+
+	if (tty_register_driver(&serial_driver)) {
+		printk("Couldn't register serial driver\n");
+		return -EIO;
+	}
+	if (tty_register_driver(&callout_driver)) {
+		tty_unregister_driver(&serial_driver);
+		printk("Couldn't register callout driver\n");
+		return -EIO;
+	}
+
+	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		info->magic = SERIAL_MAGIC;
+		info->line = i;
+		info->port = 0;
+		info->tty = 0;
+		info->type = PORT_UNKNOWN;
+		info->custom_divisor = 0;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->callout_termios =callout_driver.init_termios;
+		info->normal_termios = serial_driver.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		info->delta_msr_wait = 0;
+		info->icount.cts = info->icount.dsr = 0;
+		info->icount.rng = info->icount.dcd = 0;
+		info->icount.rx = info->icount.tx = 0;
+		info->icount.frame = info->icount.parity = 0;
+		info->icount.overrun = info->icount.brk = 0;
+		info->next_port = 0;
+		info->prev_port = 0;
+	}
+
+#ifndef MODULE
+	switch (m68k_machtype) {
+	case MACH_ATARI:
+#ifdef CONFIG_ATARI_SCC
+	    atari_SCC_init();
+#endif
+#ifdef CONFIG_ATARI_MFPSER
+	    atari_MFPser_init();
+#endif
+#ifdef CONFIG_ATARI_MIDI
+	    atari_MIDI_init();
+#endif
+	    break;
+
+	case MACH_AMIGA:
+#ifdef CONFIG_AMIGA_BUILTIN_SERIAL
+	    amiga_serinit();
+#endif
+#ifdef CONFIG_GVPIOEXT
+	    ioext_init();
+#endif
+#ifdef CONFIG_MULTIFACE_III_TTY
+	    multiface_init();
+#endif
+#ifdef CONFIG_WHIPPET_SERIAL
+            whippet_init();
+#endif
+#ifdef CONFIG_HYPERCOM1
+	    hypercom1_init();
+#endif
+	    break;
+	case MACH_MVME147:
+#ifdef CONFIG_MVME147_SCC
+	    m147_SCC_init();
+#endif
+	    break;
+	case MACH_MVME16x:
+#ifdef CONFIG_MVME162_SCC
+	    mvme_SCC_init();
+#endif
+	    break;
+	case MACH_BVME6000:
+#ifdef CONFIG_BVME6000_SCC
+	    bvme_SCC_init();
+#endif
+	    break;
+	case MACH_MAC:
+#ifdef CONFIG_MAC_SCC
+	    mac_SCC_init();
+#endif
+	case MACH_HP300:
+#ifdef CONFIG_HPDCA
+	    hpdca_init();
+#endif
+	} /* end switch on machine type */
+#endif
+
+	return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ *
+ * ++roman: Removed PCish stuff from here... New calling interface: The caller
+ * can set req->line to a value >= 0 to request a particular slot. If
+ * req->line is < 0, the first free slot is assigned. Other fields that must
+ * be filled in before the call are 'port' and 'type'.
+ */
+int m68k_register_serial(struct serial_struct *req)
+{
+	int i;
+	unsigned long flags;
+	struct m68k_async_struct *info;
+
+	save_flags(flags);
+	cli();
+#if !defined(__mc68000__) && !defined(CONFIG_APUS)
+	for (i = 0; i < NR_PORTS; i++) {
+		if (rs_table[i].port == req->port)
+			break;
+	}
+#else
+	if (req->line >= 0) {
+		if (req->line >= NR_PORTS)
+		    return -EINVAL;
+		if (rs_table[req->line].type != PORT_UNKNOWN ||
+		    rs_table[req->line].count > 0) {
+		    /* already allocated */
+		    restore_flags(flags);
+		    return -EBUSY;
+		}
+		i = req->line;
+	}
+	else {
+		for (i = 0; i < NR_PORTS; i++)
+			if ((rs_table[i].type == PORT_UNKNOWN) &&
+			    (rs_table[i].count == 0))
+				break;
+	}
+#endif
+	if (i == NR_PORTS) {
+		restore_flags(flags);
+		return -EAGAIN;
+	}
+	info = &rs_table[i];
+	if (rs_table[i].count) {
+		restore_flags(flags);
+		printk("Couldn't configure serial #%d (port=%d,irq=%d): "
+		       "device already open\n", i, req->port, req->irq);
+		return -EBUSY;
+	}
+#if !defined(__mc68000__) && !defined(CONFIG_APUS)
+	info->irq = req->irq;
+	info->port = req->port;
+	autoconfig(info);
+	if (info->type == PORT_UNKNOWN) {
+		restore_flags(flags);
+		printk("register_serial(): autoconfig failed\n");
+		return -1;
+	}
+	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
+	       info->port, info->irq);
+#else
+	info->port = req->port;
+	info->type = req->type;
+	printk(KERN_INFO "ttyS%d at 0x%08x: ", info->line, info->port);
+#endif
+	if (info->type >= 0 && info->type < PORT_MAX)
+	    printk( "%s\n", serialtypes[info->type] );
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+	else if (info->type >= 100 && info->type < 100 + M68K_PORT_MAX)
+	    printk( "%s\n", serialtypes68k[info->type - 100] );
+#endif
+	else
+	    printk( "\n" );
+	restore_flags(flags);
+	return info->line;
+}
+
+void m68k_unregister_serial(int line)
+{
+	unsigned long flags;
+	struct m68k_async_struct *info = &rs_table[line];
+
+	save_flags(flags);
+	cli();
+	if (info->tty)
+		tty_hangup(info->tty);
+	info->type = PORT_UNKNOWN;
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+	info->port = 0;
+#endif
+	printk(KERN_INFO "tty%02d unloaded\n", info->line);
+	restore_flags(flags);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return m68k_rs_init();
+}
+
+void cleanup_module(void)
+{
+	if (tty_unregister_driver(&serial_driver))
+		printk("SERIAL: failed to unregister serial driver\n");
+	if (tty_unregister_driver(&callout_driver))
+		printk("SERIAL: failed to unregister callout driver\n");
+}
+#endif /* MODULE */
+
+
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+#ifdef CONFIG_SERIAL_CONSOLE
+
+static kdev_t serial_console_device(struct console *c)
+{
+	return MKDEV(TTY_MAJOR, 64 + c->index);
+}
+
+extern void amiga_serial_console_write(struct console *co, const char *s, unsigned int count);
+extern int amiga_serial_console_wait_key(struct console *co);
+extern void amiga_init_serial_console(struct async_struct *info, int cflag);
+
+extern void atari_mfp_console_write (struct console *co, const char *str, unsigned int count);
+extern int atari_mfp_console_wait_key(struct console *co);
+extern void atari_init_mfp_port( int cflag );
+
+extern void atari_scc_console_write (struct console *co, const char *str, unsigned int count);
+extern int atari_scc_console_wait_key(struct console *co);
+extern void atari_init_scc_port( int cflag );
+
+extern void atari_midi_console_write (struct console *co, const char *str, unsigned int count);
+extern int atari_midi_console_wait_key(struct console *co);
+extern void atari_init_midi_port( int cflag );
+
+extern void mac_sccb_console_write (struct console *co, const char *str, unsigned int count);
+extern int  mac_sccb_console_wait_key(struct console *co);
+extern void mac_init_sccb_port( int cflag );
+
+extern void mac_scca_console_write (struct console *co, const char *str, unsigned int count);
+extern int  mac_scca_console_wait_key(struct console *co);
+extern void mac_init_scca_port( int cflag );
+
+extern void mvme147_init_console_port (struct console *co, int cflag);
+extern void mvme16x_init_console_port (struct console *co, int cflag);
+extern void bvme6000_init_console_port (struct console *co, int cflag);
+
+extern void hpdca_serial_console_write(struct console *co, const char *str, unsigned int count);
+extern int hpdca_serial_console_wait_key(struct console *co);
+extern int hpdca_init_serial_console(int cflag);
+
+/*
+ *	Setup initial baud/bits/parity.
+ */
+__initfunc(static int serial_console_setup(struct console *co, char *options))
+{
+	char *s;
+	int baud = 0, bits, parity;
+	int cflag = CREAD | HUPCL | CLOCAL;
+
+	bits = 8;
+	parity = 'n';
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s) parity = *s++;
+		if (*s) bits   = *s - '0';
+	}
+
+	/* Now construct a cflag setting. */
+	switch(baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 2400:
+			cflag |= B2400;
+			break;
+		case 4800:
+			cflag |= B4800;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 57600:
+			cflag |= B57600;
+			break;
+		case 9600:
+		default:
+			cflag |= B9600;
+			break;
+	}
+	switch(bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		case 8:
+		default:
+			cflag |= CS8;
+			break;
+	}
+	switch(parity) {
+		case 'o': case 'O':
+			cflag |= PARENB|PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+	co->cflag = cflag;
+	
+	/* Initialization of the port and filling in the missing fields of the
+	 * sercons struct must happen here, since register_console() already uses
+	 * write to print the log buffer. */
+	
+	/* Currently this supports the Amiga builtin port only */
+	if (MACH_IS_AMIGA && co->index == 0) {
+		co->write = amiga_serial_console_write;
+		co->wait_key = amiga_serial_console_wait_key;
+		/* no initialization yet */
+		/* amiga_init_serial_console(rs_table+co->index, */
+		/*			     serial_console_cflag); */
+	}
+	/* On Atari, Modem1 (ttyS0), Modem2 (ttyS1) and MIDI (ttyS5) are supported
+	 * Note: On a TT, 57.6 and 115.2 kbps are not possible and are replaced by
+	 * 76.8 and 153.6 kbps.
+	 * Note2: On MIDI, 7812.5 bps is selected by 4800 on the command line, and
+	 * 500 kbps by 115200. All other rates give standard 31250bps. Mode 7N is
+	 * not possible and replaced by 7O2 ... */
+	else if (MACH_IS_ATARI && co->index == 0) {
+		co->write = atari_mfp_console_write;
+		co->wait_key = atari_mfp_console_wait_key;
+		atari_init_mfp_port( cflag );
+	}
+	else if (MACH_IS_ATARI && co->index == 1) {
+		co->write = atari_scc_console_write;
+		co->wait_key = atari_scc_console_wait_key;
+		atari_init_scc_port( cflag );
+	}
+	else if (MACH_IS_ATARI && co->index == 5) {
+		co->write = atari_midi_console_write;
+		co->wait_key = atari_midi_console_wait_key;
+		atari_init_midi_port( cflag );
+	}
+	else if (MACH_IS_MVME147 && co->index == 0) {
+		mvme147_init_console_port (co, cflag);
+	}
+	else if (MACH_IS_MAC && co->index == 0) {
+		co->write = mac_scca_console_write;
+		co->wait_key = mac_scca_console_wait_key;
+		mac_init_scca_port( cflag );
+	}
+	else if (MACH_IS_MAC && co->index == 1) {
+		co->write = mac_sccb_console_write;
+		co->wait_key = mac_sccb_console_wait_key;
+		mac_init_sccb_port( cflag );
+	}
+	else if (MACH_IS_MVME16x && co->index == 0) {
+		mvme16x_init_console_port (co, cflag);
+	}
+	else if (MACH_IS_BVME6000 && co->index == 0) {
+		bvme6000_init_console_port (co, cflag);
+	}
+	return( 0 );
+}
+
+static void dummy_console_write( struct console *co, const char *str,
+								 unsigned int count )
+{
+}
+
+static int dummy_wait_key( struct console *co )
+{
+	return( '\r' );
+}
+
+static struct console sercons = {
+	"ttyS",
+	dummy_console_write,	/* filled in by serial_console_setup */
+	NULL,
+	serial_console_device,
+	dummy_wait_key,			/* filled in by serial_console_setup */
+	NULL,
+	serial_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+/*
+ * This is here to set the speed etc. for a non-initialized
+ * line. We have no termios struct yet, so we just use "cflag".
+ */
+long m68k_serial_console_init(long kmem_start, long kmem_end)
+{
+	/* I've opted to init the HP serial console here. Doing it in
+	 * serial_console_setup (above) is clearly far too late,
+	 * and in the machine-dependent setup is too early.
+	 * This may not be precisely correct or very clean but it's
+	 * easy, and I'm not going to spend any time fixing m68kserial.c
+	 * because it's going to go away soon.
+	 * 	-- Peter Maydell <pmaydell@chiark.greenend.org.uk>
+	 */
+#ifdef CONFIG_HPDCA
+	if (MACH_IS_HP300) {
+		if (hpdca_init_serial_console(B9600|CS8)) {
+			sercons.write = hpdca_serial_console_write;
+			sercons.wait_key = hpdca_serial_console_wait_key;
+		}
+	}
+#endif
+	register_console(&sercons);
+	return kmem_start;
+}
+#endif /* CONFIG_SERIAL_CONSOLE */
diff -urN linux-2.2.18-pre15/drivers/char/mac_SCC.c linux/drivers/char/mac_SCC.c
--- linux-2.2.18-pre15/drivers/char/mac_SCC.c	Fri Dec 18 09:36:05 1998
+++ linux/drivers/char/mac_SCC.c	Mon Dec  3 20:34:10 2001
@@ -46,7 +46,7 @@
 #include <linux/interrupt.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-#include <linux/serial.h>
+#include <linux/m68kserial.h>
 #include <linux/config.h>
 #include <linux/major.h>
 #include <linux/string.h>
@@ -61,6 +61,7 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/macints.h>
+#include <asm/macintosh.h>
 #ifndef CONFIG_MAC
 #include <asm/prom.h>
 #endif
@@ -708,8 +709,6 @@
 
 static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
 {
-	static int got_autovector = 0;
-
 #ifdef SCC_DEBUG
 	printk("mac_SCC: init_port, info %x \n", info);
 #endif
@@ -729,16 +728,6 @@
 	 * mac_SCC_interrupt with the proper arguments ...
 	 */
 
-	if (!got_autovector) {
-		if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info))
-			panic("macserial: can't get irq %d", IRQ4);
-#ifdef SCC_DEBUG
-		printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n",
-			IRQ4, channel, info);
-#endif
-		got_autovector = 1;
-	}
-
 	if (info->private->zs_chan_a == info->private->zs_channel) {
 		/* Channel A */
 		if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info))
@@ -1129,7 +1118,14 @@
 static void SCC_get_serial_info(struct m68k_async_struct * info,
 			   struct serial_struct * retinfo)
 {
+	retinfo->type = info->type;
+	retinfo->line = info->line;
+	retinfo->port = info->port;
+	retinfo->irq  = info->irq;
+	retinfo->flags= info->flags;
 	retinfo->baud_base = info->baud_base;
+	retinfo->close_delay=info->close_delay;
+	retinfo->closing_wait=info->closing_wait;
 	retinfo->custom_divisor = info->custom_divisor;
 }
 
@@ -1234,7 +1230,20 @@
 			copy_to_user((struct m68k_async_struct *) arg,
 				      info, sizeof(struct m68k_async_struct));
 			return 0;
-			
+	        case TIOCGSERIAL:
+		{
+			struct serial_struct tmp;
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+					    sizeof(struct serial_struct));
+			if (error)
+				return error;
+			if (!arg)
+				return -EFAULT;
+			memset(&tmp, 0, sizeof(tmp));
+			SCC_get_serial_info(info, &tmp);
+			return copy_to_user((struct serial_struct *)arg,
+					    &tmp, sizeof(tmp));
+		}
 		default:
 			return -ENOIOCTLCMD;
 		}
diff -urN linux-2.2.18-pre15/drivers/char/q40_keyb.c linux/drivers/char/q40_keyb.c
--- linux-2.2.18-pre15/drivers/char/q40_keyb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/q40_keyb.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,471 @@
+/*
+ * linux/drivers/char/q40_keyb.c
+ *
+ */
+
+#include <linux/config.h>
+
+#include <asm/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/delay.h>
+#include <linux/sysrq.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
+
+#include <asm/keyboard.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/q40_master.h>
+#include <asm/irq.h>
+#include <asm/q40ints.h>
+
+/* Some configuration switches are present in the include file... */
+
+#define KBD_REPORT_ERR
+
+/* Simple translation table for the SysRq keys */
+
+#define SYSRQ_KEY 0x54
+
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char q40kbd_sysrq_xlate[128] =
+	"\000\0331234567890-=\177\t"			/* 0x00 - 0x0f */
+	"qwertyuiop[]\r\000as"				/* 0x10 - 0x1f */
+	"dfghjkl;'`\000\\zxcv"				/* 0x20 - 0x2f */
+	"bnm,./\000*\000 \000\201\202\203\204\205"	/* 0x30 - 0x3f */
+	"\206\207\210\211\212\000\000789-456+1"		/* 0x40 - 0x4f */
+	"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
+	"\r\000/";					/* 0x60 - 0x6f */
+#endif
+
+/* Q40 uses AT scancodes - no way to change it. so we have to translate ..*/
+/* 0x00 means not a valid entry or no conversion known                    */
+
+unsigned static char q40cl[256] =
+{/* 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   a,   b,   c,   d,   e,   f, */
+ 0x00,0x43,0x00,0x3f,0x3d,0x3b,0x3c,0x58,0x00,0x44,0x42,0x40,0x3e,0x0f,0x29,0x00,     /* 0x00 - 0x0f */
+ 0x00,0x38,0x2a,0x00,0x1d,0x10,0x02,0x00,0x00,0x00,0x2c,0x1f,0x1e,0x11,0x03,0x00,     /* 0x10 - 0x1f */
+ 0x00,0x2e,0x2d,0x20,0x12,0x05,0x04,0x00,0x21,0x39,0x2f,0x21,0x14,0x13,0x06,0x00,     /* 0x20 - 0x2f  'f' is at 0x2b, what is 0x28 ???*/
+ 0x00,0x31,0x30,0x23,0x22,0x15,0x07,0x00,0x24,0x00,0x32,0x24,0x16,0x08,0x09,0x00,     /* 0x30 - 0x3f */
+ 0x00,0x33,0x25,0x17,0x18,0x0b,0x0a,0x00,0x00,0x34,0x35,0x26,0x27,0x19,0x0c,0x00,     /* 0x40 - 0x4f */
+ 0x00,0x00,0x28,0x00,0x1a,0x0d,0x00,0x00,0x3a,0x36,0x1c,0x1b,0x00,0x2b,0x00,0x00,     /* 0x50 - 0x5f*/
+ 0x00,0x56,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00,     /* 0x60 - 0x6f */
+ 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x00,     /* 0x70 - 0x7f */
+ 0x00,0x00,0x00,0x41,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x80 - 0x8f  0x84/0x37 is SySrq*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x90 - 0x9f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xa0 - 0xaf */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xb0 - 0xbf */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xc0 - 0xcf */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xd0 - 0xdf */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xe0 - 0xef */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xf0 - 0xff */
+};
+
+/* another table, AT 0xe0 codes to PC 0xe0 codes, 
+   0xff special entry for SysRq - DROPPED right now  */
+static unsigned char q40ecl[]=
+{/* 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   a,   b,   c,   d,   e,   f, */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x00 - 0x0f*/
+ 0x00,0x38,0x2a,0x00,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x10 - 0x1f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x20 - 0x2f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x30 - 0x3f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x00,0x00,     /* 0x40 - 0x4f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x1c,0x00,0x00,0x00,0x00,0x00,     /* 0x50 - 0x5f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00,     /* 0x60 - 0x6f*/
+ 0x52,0x53,0x50,0x00,0x4d,0x48,0x00,0x00,0x00,0x00,0x51,0x00,0x00,0x49,0x00,0x00,     /* 0x70 - 0x7f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x80 - 0x8f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0x90 - 0x9f*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xa0 - 0xaf*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xb0 - 0xbf*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xc0 - 0xcf*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xd0 - 0xdf*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 0xe0 - 0xef*/
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00      /* 0xf0 - 0xff*/
+};
+
+
+spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
+
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ * The keycodes 1-88,96-111,119 are fairly standard, and
+ * should probably not be changed - changing might confuse X.
+ * X also interprets scancode 0x5d (KEY_Begin).
+ *
+ * For 1-88 keycode equals scancode.
+ */
+
+#define E0_KPENTER 96
+#define E0_RCTRL   97
+#define E0_KPSLASH 98
+#define E0_PRSCR   99
+#define E0_RALT    100
+#define E0_BREAK   101  /* (control-pause) */
+#define E0_HOME    102
+#define E0_UP      103
+#define E0_PGUP    104
+#define E0_LEFT    105
+#define E0_RIGHT   106
+#define E0_END     107
+#define E0_DOWN    108
+#define E0_PGDN    109
+#define E0_INS     110
+#define E0_DEL     111
+
+#define E1_PAUSE   119
+
+/*
+ * The keycodes below are randomly located in 89-95,112-118,120-127.
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85           /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86     124
+/* tfj@olivia.ping.dk:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+  RGN1, RGN2, RGN3, RGN4, 0, 0, 0,                   /* 0x59-0x5f */
+  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */
+  0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,          /* 0x68-0x6f */
+  0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3,    /* 0x70-0x77 */
+  FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,        /* 0x78-0x7b */
+  FOCUS_PF8, JAP_86, FOCUS_PF10, 0                   /* 0x7c-0x7f */
+};
+
+/* BTC */
+#define E0_MACRO   112
+/* LK450 */
+#define E0_F13     113
+#define E0_F14     114
+#define E0_HELP    115
+#define E0_DO      116
+#define E0_F17     117
+#define E0_KPMINPLUS 118
+/*
+ * My OmniKey generates e0 4c for  the "OMNI" key and the
+ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
+ */
+#define E0_OK	124
+/*
+ * New microsoft keyboard is rumoured to have
+ * e0 5b (left window button), e0 5c (right window button),
+ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW	125
+#define E0_MSRW	126
+#define E0_MSTM	127
+
+/* this can be changed using setkeys : */
+static unsigned char e0_keys[128] = {
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x00-0x07 */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x08-0x0f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x10-0x17 */
+  0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,	      /* 0x18-0x1f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x20-0x27 */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x28-0x2f */
+  0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,	      /* 0x30-0x37 */
+  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,	      /* 0x38-0x3f */
+  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,	      /* 0x40-0x47 */
+  E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
+  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,	      /* 0x50-0x57 */
+  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,	      /* 0x58-0x5f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x60-0x67 */
+  0, 0, 0, 0, 0, 0, 0, E0_MACRO,		      /* 0x68-0x6f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x70-0x77 */
+  0, 0, 0, 0, 0, 0, 0, 0			      /* 0x78-0x7f */
+};
+
+static unsigned int prev_scancode = 0;   /* remember E0, E1 */
+
+int q40kbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+	if (scancode < SC_LIM || scancode > 255 || keycode > 127)
+	  return -EINVAL;
+	if (scancode < 128)
+	  high_keys[scancode - SC_LIM] = keycode;
+	else
+	  e0_keys[scancode - 128] = keycode;
+	return 0;
+}
+
+int q40kbd_getkeycode(unsigned int scancode)
+{
+	return
+	  (scancode < SC_LIM || scancode > 255) ? -EINVAL :
+	  (scancode < 128) ? high_keys[scancode - SC_LIM] :
+	    e0_keys[scancode - 128];
+}
+
+
+#define disable_keyboard()	
+#define enable_keyboard()	
+
+
+
+int q40kbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+	if (scancode == 0xff) {
+		/* in scancode mode 1, my ESC key generates 0xff */
+		/* the calculator keys on a FOCUS 9000 generate 0xff */
+#ifndef KBD_IS_FOCUS_9000
+#ifdef KBD_REPORT_ERR
+		if (!raw_mode)
+		  printk(KERN_DEBUG "Keyboard error\n");
+#endif
+#endif
+		prev_scancode = 0;
+		return 0;
+	}
+
+	if (scancode == 0xe0 || scancode == 0xe1) {
+		prev_scancode = scancode;
+		return 0;
+ 	}
+ 	return 1;
+}
+
+int q40kbd_translate(unsigned char scancode, unsigned char *keycode,
+		    char raw_mode)
+{
+  /*printk("translate ...\n");*/
+	if (prev_scancode) {
+	  /*
+	   * usually it will be 0xe0, but a Pause key generates
+	   * e1 1d 45 e1 9d c5 when pressed, and nothing when released
+	   */
+	  if (prev_scancode != 0xe0) {
+	      if (prev_scancode == 0xe1 && scancode == 0x1d) {
+		  prev_scancode = 0x100;
+		  return 0;
+	      } else if (prev_scancode == 0x100 && scancode == 0x45) {
+		  *keycode = E1_PAUSE;
+		  prev_scancode = 0;
+	      } else {
+#ifdef KBD_REPORT_UNKN
+		  if (!raw_mode)
+		    printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
+#endif
+		  prev_scancode = 0;
+		  return 0;
+	      }
+	  } else {
+	      prev_scancode = 0;
+	      /*
+	       *  The keyboard maintains its own internal caps lock and
+	       *  num lock statuses. In caps lock mode E0 AA precedes make
+	       *  code and E0 2A follows break code. In num lock mode,
+	       *  E0 2A precedes make code and E0 AA follows break code.
+	       *  We do our own book-keeping, so we will just ignore these.
+	       */
+	      /*
+	       *  For my keyboard there is no caps lock mode, but there are
+	       *  both Shift-L and Shift-R modes. The former mode generates
+	       *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
+	       *  So, we should also ignore the latter. - aeb@cwi.nl
+	       */
+	      if (scancode == 0x2a || scancode == 0x36)
+		return 0;
+
+	      if (e0_keys[scancode])
+		*keycode = e0_keys[scancode];
+	      else {
+#ifdef KBD_REPORT_UNKN
+		  if (!raw_mode)
+		    printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
+			   scancode);
+#endif
+		  return 0;
+	      }
+	  }
+	} else if (scancode >= SC_LIM) {
+	    /* This happens with the FOCUS 9000 keyboard
+	       Its keys PF1..PF12 are reported to generate
+	       55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
+	       Moreover, unless repeated, they do not generate
+	       key-down events, so we have to zero up_flag below */
+	    /* Also, Japanese 86/106 keyboards are reported to
+	       generate 0x73 and 0x7d for \ - and \ | respectively. */
+	    /* Also, some Brazilian keyboard is reported to produce
+	       0x73 and 0x7e for \ ? and KP-dot, respectively. */
+
+	  *keycode = high_keys[scancode - SC_LIM];
+
+	  if (!*keycode) {
+	      if (!raw_mode) {
+#ifdef KBD_REPORT_UNKN
+		  printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"
+			 " - ignored\n", scancode);
+#endif
+	      }
+	      return 0;
+	  }
+ 	} else
+	  *keycode = scancode;
+ 	return 1;
+}
+
+char q40kbd_unexpected_up(unsigned char keycode)
+{
+	/* unexpected, but this can happen: maybe this was a key release for a
+	   FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */
+	if (keycode >= SC_LIM || keycode == 85)
+	    return 0;
+	else
+	    return 0200;
+}
+
+static int keyup=0;
+static int qprev=0;
+
+static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	unsigned char status;
+
+	disable_keyboard();
+	spin_lock_irqsave(&kbd_controller_lock, flags);
+	kbd_pt_regs = regs;
+
+	status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG);
+	if (status ) 
+	  {
+	    unsigned char scancode,qcode;
+	    
+	    qcode = master_inb(KEYCODE_REG);
+	    
+	    if (qcode != 0xf0)
+	      {
+		if (qcode == 0xe0)
+		  {
+		    qprev=0xe0;
+		    handle_scancode(qprev);
+		    goto exit;
+		  }
+		
+		scancode=qprev ? q40ecl[qcode] : q40cl[qcode];
+#if 0
+/* next line is last resort to hanlde some oddities */
+		if (qprev && !scancode) scancode=q40cl[qcode];
+#endif
+		qprev=0;
+		if (!scancode)
+		  {
+		    printk("unknown scancode %x\n",qcode);
+		    goto exit;
+		  }
+		if (scancode==0xff)  /* SySrq */
+		  scancode=SYSRQ_KEY;
+
+		handle_scancode(scancode | (keyup ? 0200 : 0));
+		keyup=0;
+		mark_bh(KEYBOARD_BH);
+
+	      }
+	    else
+	      keyup=1;
+	  }
+exit:
+	spin_unlock_irqrestore(&kbd_controller_lock, flags);
+	master_outb(-1,KEYBOARD_UNLOCK_REG); /* keyb ints reenabled herewith */
+	enable_keyboard();
+}
+
+
+
+
+#ifdef CONFIG_MAGIC_SYSRQ
+int kbd_is_sysrq(unsigned char keycode)
+{
+	return( keycode == SYSRQ_KEY );
+}
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+
+
+
+#define KBD_NO_DATA	(-1)	/* No data */
+#define KBD_BAD_DATA	(-2)	/* Parity or other error */
+
+static int __init kbd_read_input(void)
+{
+	int retval = KBD_NO_DATA;
+	unsigned char status;
+
+	status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG);
+	if (status) {
+		unsigned char data = master_inb(KEYCODE_REG);
+
+		retval = data;
+		master_outb(-1,KEYBOARD_UNLOCK_REG);
+	}
+	return retval;
+}
+
+extern void q40kbd_leds(unsigned char leds)
+{ /* nothing can be done */ }
+
+static void __init kbd_clear_input(void)
+{
+	int maxread = 100;	/* Random number */
+
+	do {
+		if (kbd_read_input() == KBD_NO_DATA)
+			break;
+	} while (--maxread);
+}
+
+
+void __init q40kbd_init_hw(void)
+{
+#if 0
+	/* Get the keyboard controller registers (incomplete decode) */
+	request_region(0x60, 16, "keyboard");
+#endif
+	/* Flush any pending input. */
+	kbd_clear_input();
+
+	/* Ok, finally allocate the IRQ, and off we go.. */
+	request_irq(Q40_IRQ_KEYBOARD, keyboard_interrupt, 0, "keyboard", NULL);
+	master_outb(-1,KEYBOARD_UNLOCK_REG);
+	master_outb(1,KEY_IRQ_ENABLE_REG);
+
+}
+
diff -urN linux-2.2.18-pre15/drivers/char/ser_hpdca.c linux/drivers/char/ser_hpdca.c
--- linux-2.2.18-pre15/drivers/char/ser_hpdca.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_hpdca.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,997 @@
+/*
+ * Driver for the 98626/98644/internal serial interface on hp300/hp400
+ * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
+ *
+ * This driver was written by Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * based on informaition gleaned from the NetBSD driver and the ser_ioext
+ * driver. Copyright(C) Peter Maydell 05/1998.
+ * The most significant difference between us and ser_ioext is that 
+ * we have to worry about UARTs with no FIFO (ignoring the Amiga vs HP differences...)
+ *
+ * We worry about multiple boards in a system because ser_ioext does and
+ * it seems the sensible thing to do. It's untested, though. Also, are we
+ * duplicating work done by the hardware-independent m68k serial layer?
+ * (multiple devices seems like an obvious thing to go for...)
+ *
+ * The driver is called hpdca because the NetBSD driver is 'dca' and 
+ * I wanted something less generic than hp300...
+ *
+ *  N.B. On the hp700 and some hp300s, there is a "secret bit" with
+ *  undocumented behavior.  The third bit of the Modem Control Register
+ *  (MCR_IEN == 0x08) must be set to enable interrupts.  Failure to do
+ *  so can result in deadlock on those machines, whereas there don't seem to
+ *  be any harmful side-effects from setting this bit on non-affected
+ *  machines.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/m68kserial.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+
+#include <asm/blinken.h>
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/hwtest.h>                           /* hwreg_present() */
+#include <asm/io.h>                               /* readb(), writeb() */
+#include <linux/dio.h>
+
+#include "ser_hpdca.h"
+
+/* Set these to 0 when the driver is finished */
+#define HPDCA_DEBUG 1
+#define DEBUG_IRQ 1
+#define DEBUG 1
+#define DEBUG_FLOW 1
+
+#define FIFO_TRIGGER_LEVEL FIFO_TRIG_8
+
+/***************************** Prototypes *****************************/
+static void hpdca_ser_interrupt(volatile struct uart_16c550 *uart, int line, 
+                                int hasfifo, int *spurious_count);
+static void ser_init( struct m68k_async_struct *info );
+static void ser_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void ser_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int  ser_check_custom_divisor(struct m68k_async_struct *info,
+				     int baud_base, int divisor);
+static void ser_change_speed( struct m68k_async_struct *info );
+static void ser_throttle( struct m68k_async_struct *info, int status );
+static void ser_set_break( struct m68k_async_struct *info, int break_flag );
+static void ser_get_serial_info( struct m68k_async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int ser_get_modem_info( struct m68k_async_struct *info );
+static int ser_set_modem_info( struct m68k_async_struct *info, int new_dtr,
+			      int new_rts );
+static void ser_stop_receive(struct m68k_async_struct *info);
+static int ser_trans_empty(struct m68k_async_struct *info);
+/************************* End of Prototypes **************************/
+
+/*
+ * SERIALSWITCH structure for the dca
+ */
+
+static SERIALSWITCH hpdca_ser_switch = {
+        ser_init,
+        ser_deinit,
+        ser_enab_tx_int,
+        ser_check_custom_divisor,
+        ser_change_speed,
+        ser_throttle,
+        ser_set_break,
+        ser_get_serial_info,
+        ser_get_modem_info,
+        ser_set_modem_info,
+        NULL,
+        ser_stop_receive, ser_trans_empty, NULL
+};
+
+/* table of divisors to use for various baud rates. DCABRD() is a macro
+ * which gives the correct ratio for the clock speed (hp300 vs hp700)
+ */
+static int hpdca_baud_table[16] = {
+ 	/* B0     */ 0, /* Never use this value !!! */
+	/* B50    */ DCABRD(50),
+	/* B75    */ DCABRD(75),
+	/* B110   */ DCABRD(110),
+	/* B134	  */ DCABRD(134),
+	/* B150	  */ DCABRD(150),
+	/* B200	  */ DCABRD(200),
+	/* B300	  */ DCABRD(300),
+	/* B600	  */ DCABRD(600),
+	/* B1200  */ DCABRD(1200),
+	/* B1800  */ DCABRD(1800),
+	/* B2400  */ DCABRD(2400),
+	/* B4800  */ DCABRD(4800),
+	/* B9600  */ DCABRD(9600),
+	/* B19200 */ DCABRD(19200),
+	/* B38400 */ DCABRD(38400)   /* The last of the standard rates.  */
+};
+
+int hpdca_num;
+hpdcaInfoType hpdca_info[MAX_HPDCA];
+
+/*
+ * Functions
+ *
+ * dev_id = hpdca_info.
+ *
+ */
+static void hpdca_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        /* We have to take this interrupt and work out which board it's for. */
+        int b;
+
+        if (hpdca_num == 0) {
+                /* This interrupt can't be for us */
+                return;
+        }
+   
+        for (b = 0; b < hpdca_num; b++) {
+                hpdca_struct *board = hpdca_info[b].board;
+                hpdcaInfoType *board_info = &hpdca_info[b];
+
+                if ((board->dca_ic & (IC_IR|IC_IE)) != (IC_IR|IC_IE))
+                        /* interrupts not enabled, not for us */
+                        continue;
+      
+                /* Service any uart irqs. */
+                hpdca_ser_interrupt(board_info->uart, board_info->line, 
+                                    board_info->hasfifo, &board_info->spurious_count);
+
+                if (board_info->spurious_count > 10000) {
+                        board->dca_ic &= ~IC_IE;
+                        printk("Too many spurious interrupts, disabling board irq\n");
+                        board_info->spurious_count = 0;
+                }
+        } /* for b */
+}
+
+/*
+** hpdca_ser_interrupt()
+** 
+** Check for and handle interrupts for this uart.
+*/
+static void hpdca_ser_interrupt(volatile struct uart_16c550 *uart, int line, 
+                                int hasfifo, int *spurious_count)
+{
+        struct m68k_async_struct *info = rs_table + line;
+
+        u_char iir;
+        u_char lsr;
+        int ch;
+        
+        iir = uart->IIR;
+
+        ++*spurious_count;
+
+#if DEBUG_IRQ
+        printk("ttyS%d: IER=%02X, IIR=%02X, LSR=%02X, MSR=%02X\n", 
+               line, uart->IER, iir, uart->LSR, uart->MSR);
+#endif
+
+        while (!(iir & IRQ_PEND)) {
+                /* IRQ for this uart */
+#if DEBUG_IRQ
+                printk("IRQ_PEND on ttyS%d...\n", line);
+#endif
+                
+                /* This really is our interrupt */
+                *spurious_count = 0;
+                
+                switch (iir & (IRQ_ID1 | IRQ_ID2 | IRQ_ID3)) {
+                case IRQ_RLS: /* Receiver Line Status */
+#if DEBUG_FLOW
+                        printk("RLS irq on ttyS%d\n", line);
+#endif
+                case IRQ_CTI: /* Character Timeout */
+                case IRQ_RDA: /* Received Data Available */
+#if DEBUG_IRQ
+                        printk("irq (IIR=%02X) on ttyS%d\n", line, iir);
+#endif
+                        /*
+                         * Copy chars to the tty-queue ...
+                         * Be careful that we aren't passing one of the
+                         * Receiver Line Status interrupt-conditions without noticing.
+                         */
+                        if (!hasfifo)
+                        {
+                                /* with no FIFO reads are trivial */
+                                ch = uart->RBR;
+                                rs_receive_char(info, ch, 0);
+#ifdef DEBUG_IRQ
+                                printk("Read a char from FIFOless uart: %02X\n", ch);
+#endif
+                        }
+                        else
+                        {
+                                int got = 0;
+
+                                lsr = uart->LSR;
+#if DEBUG_IRQ
+                                printk("uart->LSR & DR = %02X\n", lsr & DR);
+#endif
+                                while (lsr & DR) {
+                                        u_char err = 0;
+                                        ch = uart->RBR;
+#if DEBUG_IRQ
+                                        printk("Read a char from the uart: %02X, lsr=%02X\n", 
+                                               ch, lsr);
+#endif
+                                        if (lsr & BI) {
+                                                err = TTY_BREAK;
+                                        }
+                                        else if (lsr & PE) {
+                                                err = TTY_PARITY;
+                                        }
+                                        else if (lsr & OE) {
+                                                err = TTY_OVERRUN;
+                                        }
+                                        else if (lsr & FE) {
+                                                err = TTY_FRAME;
+                                        }
+#if DEBUG_IRQ
+                                        printk("rs_receive_char(ch=%02X, err=%02X)\n", ch, err);
+#endif
+                                        rs_receive_char(info, ch, err);
+                                        got++;
+                                        lsr = uart->LSR;
+                                }
+#if DEBUG_FLOW
+                                printk("[%d<]", got);
+#endif
+                        }
+                        break;
+
+                case IRQ_THRE: /* Transmitter holding register empty */
+                {
+                        int fifo_space = (hasfifo ? 16 : 1);      /* no FIFO => send one char */
+                        int sent = 0;
+                        
+#if DEBUG_IRQ
+                        printk("THRE-irq for ttyS%d\n", line);
+#endif
+
+                        /* If the uart is ready to receive data and there are chars in */
+                        /* the queue we transfer all we can to the uart's FIFO         */
+                        if (info->xmit_cnt <= 0 || info->tty->stopped ||
+                            info->tty->hw_stopped) {
+                                /* Disable transmitter empty interrupt */
+                                uart->IER &= ~(ETHREI);
+                                /* Need to send a char to acknowledge the interrupt */
+                                uart->THR = 0;
+#if DEBUG_FLOW
+                                if (info->tty->hw_stopped) {
+                                        printk("[-]");
+                                }
+                                if (info->tty->stopped) {
+                                        printk("[*]");
+                                }
+#endif
+                                break;
+                        }
+
+                        /* Handle software flow control */
+                        if (info->x_char) {
+#if DEBUG_FLOW
+                                printk("[^%c]", info->x_char + '@');
+#endif
+                                uart->THR = info->x_char;
+                                info->x_char = 0;
+                                fifo_space--;
+                                sent++;
+                        }
+
+                        /* Fill the fifo */
+                        while (fifo_space > 0) {
+                                fifo_space--;
+#if DEBUG_IRQ
+                                printk("Sending %02x to the uart.\n", 
+                                       info->xmit_buf[info->xmit_tail]);
+#endif
+                                uart->THR = info->xmit_buf[info->xmit_tail++];
+                                sent++;
+                                info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+                                if (--info->xmit_cnt == 0) {
+                                        break;
+                                }
+                        }
+#if DEBUG_FLOW
+                        printk("[>%d]", sent);
+#endif
+
+                        if (info->xmit_cnt == 0) {
+#if DEBUG_IRQ
+                                printk("Sent last char - turning off THRE interrupts\n");
+#endif
+                                /* Don't need THR interrupts any more */
+                                uart->IER &= ~(ETHREI);
+                        }
+
+                        if (info->xmit_cnt < WAKEUP_CHARS) {
+                                rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+                        }
+                }
+                break;
+
+                case IRQ_MS: /* Must be modem status register interrupt? */
+                {
+                        u_char msr = uart->MSR;
+#if DEBUG_IRQ
+                        printk("MS-irq for ttyS%d: %02x\n", line, msr);
+#endif
+
+                        if (info->flags & ASYNC_INITIALIZED) {
+                                if (msr & DCTS) {
+                                        rs_check_cts(info, (msr & CTS)); /* active high */
+#if DEBUG_FLOW
+                                        printk("[%c-%d]", (msr & CTS) ? '^' : 'v', 
+                                               info->tty ? info->tty->hw_stopped : -1);
+#endif
+                                }
+                                if (msr & DDCD) {
+#if DEBUG
+                                        printk("rs_dcd_changed(%d)\n", !(msr & DCD));
+#endif
+                                        rs_dcd_changed(info, !(msr & DCD)); /* active low */
+                                }
+                        }
+                }
+                break;
+
+                default:
+#if DEBUG_IRQ
+                        printk("Unexpected irq for ttyS%d\n", line);
+#endif
+                        break;
+                } /* switch (iir) */
+                iir = uart->IIR;
+        } /* while IRQ_PEND */
+}
+
+static void ser_init(struct m68k_async_struct *info)
+{
+#if DEBUG
+	printk("ser_init\n");
+#endif
+
+	while ((curruart(info)->LSR) & DR) {
+#if HPDCA_DEBUG
+		printk("Emptying uart\n");
+#endif
+		(void)curruart(info)->RBR;
+	}
+
+	/* Set DTR and RTS */
+	curruart(info)->MCR |= (DTR | RTS | OUT2);
+
+        /* Enable interrupts. IF_EXTER irq has already been enabled in hpdca_init()*/
+        /* DON'T enable ETHREI here because there is nothing to send yet (murray) */
+	curruart(info)->IER |= (ERDAI | ELSI | EMSI);
+
+	MOD_INC_USE_COUNT;
+}
+
+
+static void ser_deinit(struct m68k_async_struct *info, int leave_dtr)
+{
+#if DEBUG
+	printk("ser_deinit\n");
+#endif
+
+	/* Wait for the uart to get empty */
+	while(!(curruart(info)->LSR & TEMT)) {
+#if HPDCA_DEBUG
+		printk("Waiting for the transmitter to finish\n");
+#endif
+	}
+
+	while(curruart(info)->LSR & DR) {
+#if HPDCA_DEBUG
+		printk("Uart not empty - flushing!\n");
+#endif
+		(void)curruart(info)->RBR;
+	}
+
+	/* No need to disable UART interrupts since this will already
+	 * have been done via ser_enab_tx_int() and ser_stop_receive()
+	 */
+
+	ser_RTSoff(info);
+	if (!leave_dtr) {
+		ser_DTRoff(info);
+	}
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+** ser_enab_tx_int()
+** 
+** Enable or disable tx interrupts.
+** Note that contrary to popular belief, it is not necessary to
+** send a character to cause an interrupt to occur. Whenever the
+** THR is empty and THRE interrupts are enabled, an interrupt will occur.
+** (murray)
+*/
+static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag)
+{
+	if (enab_flag) {
+		curruart(info)->IER |= ETHREI;
+	}
+	else {
+		curruart(info)->IER &= ~(ETHREI);
+	}
+}
+
+static int  ser_check_custom_divisor(struct m68k_async_struct *info,
+				     int baud_base, int divisor)
+{
+	/* Always return 0 or else setserial spd_hi/spd_vhi doesn't work */
+	return 0;
+}
+
+static void ser_change_speed(struct m68k_async_struct *info)
+{
+	unsigned int cflag, baud, chsize, stopb, parity, aflags;
+	unsigned int div = 0, ctrl = 0;
+
+#if DEBUG
+	printk("ser_change_speed\n");
+#endif
+
+	if (!info->tty || !info->tty->termios) 
+                return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+#if DEBUG
+	printk("Changing to baud-rate %i\n", baud);
+#endif
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 4)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+        
+        /* Maximum speed is 38400 */
+        if (baud > 15) 
+                baud = 15;
+        div = hpdca_baud_table[baud];
+
+	if (!div) {
+                /* speed == 0 -> drop DTR */
+#if DEBUG
+                printk("Dropping DTR\n");
+#endif
+                ser_DTRoff(info);
+                return;
+	}
+
+        /*
+         * We have to set DTR when a valid rate is chosen, otherwise DTR
+         * might get lost when programs use this sequence to clear the line:
+         *
+         * change_speed(baud = B0);
+         * sleep(1);
+         * change_speed(baud = Bx); x != 0
+         *
+         * The pc-guys do this as well.
+         */
+	ser_DTRon(info);
+        
+	if (chsize == CS8) {
+#if DEBUG
+		printk("Setting serial word-length to 8-bits\n");
+#endif
+		ctrl |= data_8bit;
+	}
+	else if (chsize == CS7) {
+#if DEBUG
+		printk("Setting serial word-length to 7-bits\n");
+#endif
+		ctrl |= data_7bit;
+	}
+	else if (chsize == CS6) {
+#if DEBUG
+		printk("Setting serial word-length to 6-bits\n");
+#endif
+		ctrl |= data_6bit;
+	}
+	else if (chsize == CS5) {
+#if DEBUG
+		printk("Setting serial word-length to 5-bits\n");
+#endif
+		ctrl |= data_5bit;
+	};
+
+
+	/* If stopb is true we set STB which means 2 stop-bits - */
+	/* otherwise we  only get 1 stop-bit.                    */
+	ctrl |= (stopb ? STB : 0);
+     
+        /* if parity disabled, ctrl |= 0
+         * if odd parity, ctrl |= PEN
+         * if even parity, ctrl |= PEN|EPS
+         * Not my code -- PMM :->
+         */
+	ctrl |= ((parity & PARENB) ? 
+                 ((parity & PARODD) ? (PEN) : (PEN | EPS)) : 0x00 ); 
+
+#if DEBUG
+	printk ("Storing serial-divisor %i\n", div);
+#endif
+
+	curruart(info)->LCR = (ctrl | DLAB);
+        
+	/* Store high byte of divisor */
+	curruart(info)->DLM = ((div >> 8) & 0xff);
+  
+	/* Store low byte of divisor */
+
+	curruart(info)->DLL = (div & 0xff);
+
+	curruart(info)->LCR = ctrl;
+}
+
+
+static void ser_throttle(struct m68k_async_struct *info, int status){
+
+#if DEBUG
+	printk("ser_throttle\n");
+#endif
+	if (status){
+		ser_RTSoff(info);
+	}
+	else{
+		ser_RTSon(info);
+	}
+}
+
+
+static void ser_set_break(struct m68k_async_struct *info, int break_flag)
+{
+#if HPDCA_DEBUG
+	printk("ser_set_break\n");
+#endif
+	if (break_flag)
+		curruart(info)->LCR |= SET_BREAK;
+	else
+		curruart(info)->LCR &= ~SET_BREAK;
+}
+
+
+static void ser_get_serial_info(struct m68k_async_struct *info,
+				struct serial_struct *retinfo)
+{
+        int b;
+   
+#if DEBUG
+	printk("ser_get_serial_info\n");
+#endif
+
+	retinfo->baud_base = HPDCA_BAUD_BASE;
+        /* This field is currently ignored by the upper layers of
+         * the serial driver, but we set it anyway :->
+         * Is this really the best way to find out which board our caller
+         * is referring to???
+         */
+        for (b = 0; b < hpdca_num; b++)
+                if (hpdca_info[b].uart == curruart(info))
+                        retinfo->xmit_fifo_size = (hpdca_info[b].hasfifo ? 16 : 1);
+
+        retinfo->custom_divisor = info->custom_divisor;
+}
+
+static unsigned int ser_get_modem_info(struct m68k_async_struct *info)
+{
+	unsigned char msr, mcr;
+
+#if DEBUG
+	printk("ser_get_modem_info\n");
+#endif
+
+	msr = curruart(info)->MSR;
+	mcr = curruart(info)->MCR; /* The DTR and RTS are located in the */
+				   /* ModemControlRegister ...           */
+
+	return(
+		((mcr & DTR) ? TIOCM_DTR : 0) |
+		((mcr & RTS) ? TIOCM_RTS : 0) |
+
+		((msr & DCD) ? 0 : TIOCM_CAR) | /* DCD is active low */
+		((msr & CTS) ? TIOCM_CTS : 0) |
+		((msr & DSR) ? TIOCM_DSR : 0) |
+		((msr & RING_I) ? TIOCM_RNG : 0)
+		);
+}
+
+static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr,
+			      int new_rts)
+{
+#if DEBUG
+	printk("ser_set_modem_info new_dtr=%i new_rts=%i\n", new_dtr, new_rts);
+#endif
+	if (new_dtr == 0)
+		ser_DTRoff(info);
+	else if (new_dtr == 1)
+		ser_DTRon(info);
+
+	if (new_rts == 0)
+		ser_RTSoff(info);
+	else {
+		if (new_rts == 1)
+			ser_RTSon(info);
+	}
+
+	return 0;
+}
+
+static void ser_stop_receive (struct m68k_async_struct *info)
+{
+	/* Disable uart receive and status interrupts */
+	curruart(info)->IER &= ~(ERDAI | ELSI | EMSI);
+}
+
+static int ser_trans_empty (struct m68k_async_struct *info)
+{
+	return (curruart(info)->LSR & THRE);
+}
+
+/*
+** init_hpdca_uart(): init the uart. Returns 1 if UART has a FIFO, 0 otherwise
+** 
+*/
+static int init_hpdca_uart(struct uart_16c550 *uart)
+{
+    /* Wait for the uart to get empty */
+        while (!(uart->LSR & TEMT)) {
+#if HPDCA_DEBUG
+                printk("Waiting for transmitter to finish\n");
+#endif
+        }
+
+        /*
+         * Disable all uart interrups (they will be re-enabled in
+         * ser_init when they are needed).
+         */
+        uart->IER = 0;
+        /* Master interrupt enable - National semconductors doesn't like
+         * to follow standards, so their version of the chip is
+         * different and I only had a copy of their data-sheets :-(
+         */
+        uart->MCR = OUT2;
+        
+        /*
+         * Set the uart to a default setting of 8N1 - 9600
+         */
+        uart->LCR = (data_8bit | DLAB);
+        uart->DLM = 0;
+        uart->DLL = 48;
+        uart->LCR = (data_8bit);
+        
+        /* Enable + reset the tx and rx FIFO's. Set the rx FIFO-trigger count.
+         * If we then find that the bits in IIR that indicate enabling of 
+         * the FIFOs are zero, we conclude that this uart has no FIFOs.
+         */
+        uart->FCR =  (FIFO_ENA | RCVR_FIFO_RES | XMIT_FIFO_RES | FIFO_TRIGGER_LEVEL);
+        udelay(100);                                  /* wait for uart to get its act together */
+        return (uart->IIR & (FIFO_ENA1 | FIFO_ENA0));
+}
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/* 
+ * Support for a serial console on the DCA port.  We used to do this in config.c
+ * so it got set up early (and even if you didn't ask for serial console), but we
+ * don't any more. (PB)    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *                               [I don't believe this!-- PMM]
+ * 
+ * However, we still want it set up earlier than the normal serial device driver,
+ * so I've added a hook in m68kserial.c's serial_console_init() which is IMHO the
+ * right place for it.     -- PMM
+ */
+static volatile struct uart_16c550 *uart = NULL;
+static int conscode;
+
+/* Warning: we get called very early, so you can't allocate memory!
+ * [we could pass this function kmem_start, kmem_end if necessary]
+ */
+__initfunc(int hpdca_init_serial_console(int cflag))
+{
+        /* here cflag encodes rqd baud rate, parity, etc */
+        int baud = cflag & CBAUD;
+        int ctrl = 0;
+        int div, scode;
+        u_char stat;
+        int dly;
+        
+        switch (cflag & CSIZE)
+        {
+        case CS5:
+                ctrl = data_5bit;
+                break;
+        case CS6:
+                ctrl = data_6bit;
+                break;
+        case CS7:
+                ctrl = data_7bit;
+                break;
+        case CS8:
+                ctrl = data_8bit;
+                break;
+        }
+        
+        ctrl |= ((cflag & PARENB) ? ((cflag & PARODD) ? 0x8 : 0x18) : 0x00);
+        
+        if (baud < B1200 || baud > B38400) 
+                baud = B9600;  /* sensible default for unimplemented rates */
+        
+        div = hpdca_baud_table[baud];
+        /* Unfortunately we have to do a complete scan of the DIO bus in 
+         * order to locate the serial port...
+         */
+        for (scode = 0; scode < DIO_SCMAX; ++scode)
+        {
+                u_char *va;
+                u_char id;
+                
+                /* skip the hole and the internal HPIB controller */
+                if (DIO_SCINHOLE(scode) || DIO_ISIHPIB(scode))
+                        continue;
+                
+                va = dio_scodetoviraddr(scode);
+                if (!va)
+                        continue;
+                
+                if (!hwreg_present(va + DIO_IDOFF))         /* nothing there */
+                        continue;
+
+                id = DIO_ID(va);                            /* get the ID byte */
+                
+                if (id != DIO_ID_DCA0 && id != DIO_ID_DCA0REM 
+                    && id != DIO_ID_DCA1 && id != DIO_ID_DCA1REM)
+                        continue;
+                
+                printk("Console is HP DCA at select code %d\n", scode);
+                /* OK, this is the DCA, set it up */
+                writeb(0xff, va);                 /* DCA reset */
+                /* should udelay(100) here */
+                for(dly = 0; dly < 10000; dly++)
+                        barrier();                /* defeat GCC optimisation! */
+                
+                uart = (struct uart_16c550 *)(va+16); /* offset of the UART on the board */
+                conscode = scode;                 /* save so we don't init this board again */
+                
+                break;
+        }
+        
+        if (!uart)                                /* no DCA detected, abort */
+                return 0;
+
+        uart->IER = 0;                            /* disable interrupts */
+        uart->MCR = OUT2;                         /* master interrupt enable */
+        uart->LCR = (ctrl | DLAB);                /* usual messing around to set divisor */
+        uart->DLM = ((div >> 8) & 0xff);
+        uart->DLL = (div & 0xff);
+        uart->LCR = ctrl;
+        for(dly = 0; dly < 10000; dly++)
+                barrier();                        /* defeat GCC optimisation! */
+        stat = uart->IIR;                         /* ack any interrupts raised */
+
+        return 1;                                 /* success */
+}
+	
+static inline void hpdca_out(char c)
+{
+        u_char stat;
+        int timo;
+      
+        timo = 50000;
+        while (!(uart->LSR & THRE) && --timo)
+                barrier();
+        uart->THR = c;
+        timo = 1500000;
+        while (!(uart->LSR & THRE) && --timo)          /* wait for Tx to complete */
+                barrier();
+        stat = uart->IIR;                             /* ack any generated interrupts */
+}
+
+
+void hpdca_serial_console_write(struct console *co, const char *str,
+				       unsigned int count)
+{
+        while (count--) {
+                if (*str == '\n')
+                        hpdca_out('\r');
+                hpdca_out(*str++);
+        }
+}
+
+int hpdca_serial_console_wait_key(struct console *co)
+{
+        u_char stat;
+
+        while(!(uart->LSR & DR))
+                barrier();
+        stat = uart->IIR;                         /* clear any generated ints */
+        return(uart->RBR);
+}
+
+#ifdef UNDEF
+static struct console hpdca_console_driver = {
+	"debug",
+	hpdca_serial_console_write,
+	NULL, /* read */
+	NULL, /* device */
+	hpdca_serial_console_wait_key,
+	NULL, /* unblank */
+	NULL, /* setup */
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+#endif /* UNDEF */
+#endif /* CONFIG_SERIAL_CONSOLE */
+
+/*
+ * Detect and initialize all HP dca boards in this system.
+ */
+__initfunc(int hpdca_init(void))
+{
+	int isr_installed = 0;
+	unsigned int scode = 0;
+	static char support_string[50] = "HP 98644A dca serial";
+        int ipl;
+
+        if (!MACH_IS_HP300)
+		return -ENODEV;
+
+	memset(hpdca_info, 0, sizeof(hpdca_info));
+
+	for (;;) {
+		hpdca_struct *board;
+		int line;
+                struct uart_16c550 *uart;
+                struct serial_struct req;
+                
+                /* We detect boards by looking for DIO boards which match a
+                 * given subset of IDs. dio_find() returns the board's scancode.
+                 * The scancode to physaddr mapping is a property of the hardware,
+                 * as is the scancode to IPL (interrupt priority) mapping.
+                 */
+                scode = dio_find(DIO_ID_DCA0);
+                if (!scode)
+                        scode = dio_find(DIO_ID_DCA0REM);
+                if (!scode)
+                        scode = dio_find(DIO_ID_DCA1);
+                if (!scode)
+                        scode = dio_find(DIO_ID_DCA1REM);
+                if (!scode)
+                        break;                    /* no, none at all */
+
+#ifdef HPDCA_DEBUG                
+                printk("Found DCA scode %d",scode);
+#endif                
+                board = (hpdca_struct *) dio_scodetoviraddr(scode);
+                ipl = dio_scodetoipl(scode);
+#ifdef HPDCA_DEBUG
+                printk(" at viraddr %08lX, ipl %d\n",(u_long)board, ipl);
+#endif
+                hpdca_info[hpdca_num].board = board;
+                hpdca_info[hpdca_num].scode = scode;
+#ifdef CONFIG_SERIAL_CONSOLE
+                if (scode == conscode)
+                {
+                        printk("Whoops, that's the console!\n");
+                        dio_config_board(scode);
+                        /* What are we actually supposed to do here??? */
+                        continue;
+                }
+#endif
+                /* Reset the DCA */
+                board->dca_reset = 0xff;
+                udelay(100);
+                
+		/* Register the serial port device. */
+                uart = &board->uart;
+
+                req.line = -1; /* first free ttyS? device */
+                req.type = SER_HPDCA;
+                req.port = (int)uart;             /* yuck */
+                if ((line = register_serial(&req)) < 0) {
+                        printk( "Cannot register HP dca serial port: no free device\n" );
+                        break;
+                }
+
+                /* Add this uart to our hpdca_info[] table */
+                hpdca_info[hpdca_num].line = line;
+                hpdca_info[hpdca_num].uart = uart;
+
+                rs_table[line].sw = &hpdca_ser_switch;
+
+                /* We don't use these values */
+                rs_table[line].nr_uarts = 0;
+                rs_table[line].board_base = board;
+                
+                /* init the UART proper and find out if it's got a FIFO */
+                hpdca_info[hpdca_num].hasfifo = init_hpdca_uart(uart);
+                
+		/* Install ISR if it hasn't been installed already */
+		if (!isr_installed) {
+			request_irq(ipl, hpdca_interrupt, 0,
+				    support_string, hpdca_info);
+			isr_installed++;
+		}
+		hpdca_num++;
+                
+                /* tell the DIO code that this board is configured */
+                dio_config_board(scode);
+        }
+
+	return(0);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return(hpdca_init());
+}
+
+void cleanup_module(void)
+{
+        int i;
+
+        for (i = 0; i < hpdca_num; i++) {
+                hpdca_struct *board = hpdca_info[i].board;
+                int j;
+
+                /* Disable board-interrupts */
+                board->dca_ic &= ~IC_IE;
+                
+                /* Disable "master" interrupt select on uart */
+                board->uart.MCR = 0;
+
+                /* Disable all uart interrupts */
+                board->uart.IER = 0;
+    
+                unregister_serial(hpdca_info[i].line);
+                
+                dio_unconfig_board(board->scode);
+        }
+        
+        if (hpdca_num != 0) {
+                /* Indicate to the IRQ handler that nothing needs to be serviced */
+                hpdca_num = 0;
+                free_irq(dio_scodetoipl(hpdca_info[0].scode), hpdca_info);
+        }
+}
+#endif
diff -urN linux-2.2.18-pre15/drivers/char/ser_hpdca.h linux/drivers/char/ser_hpdca.h
--- linux-2.2.18-pre15/drivers/char/ser_hpdca.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_hpdca.h	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,91 @@
+/*
+ * Defines for the 98626/98644/internal serial interface on hp300/hp400
+ * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
+ *
+ * This driver was written by Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * based on informaition gleaned from the NetBSD driver and the ser_ioext
+ * driver. Copyright(C) 05/1998.
+ *
+ * The driver is called hpdca because the NetBSD driver is 'dca' and 
+ * I wanted something less generic than hp300...
+ *
+ * The NetBSD driver works for hp700 as well. I don't have one so I
+ * took the easy route and dropped support. However, this might be a good
+ * place to start if you're adding it back in again...
+ *
+ *  N.B. On the hp700 and some hp300s, there is a "secret bit" with
+ *  undocumented behavior.  The third bit of the Modem Control Register
+ *  (MCR_IEN == 0x08) must be set to enable interrupts.  Failure to do
+ *  so can result in deadlock on those machines, whereas there don't seem to
+ *  be any harmful side-effects from setting this bit on non-affected
+ *  machines.
+ */
+
+#ifndef _SER_HPDCA_H_
+#define _SER_HPDCA_H_
+
+/* 16bit baud rate divisor (lower byte in dca_data, upper in dca_ier).
+ * NB:the hp300 constant is for a clk frequency of 2.4576
+ * ie HPDCA_BAUD_BASE = 2457600 / 16
+ */
+#define HPDCA_BAUD_BASE 153600
+#define DCABRD(x) (HPDCA_BAUD_BASE / (x))
+
+/* interface reset/id (300 only) */
+#define DCAID0          0x02
+#define DCAREMID0       0x82
+#define DCAID1          0x42
+#define DCAREMID1       0xC2
+
+/* interrupt control (300 only) */
+#define DCAIPL(x)       ((((x) >> 4) & 3) + 3)
+#define IC_IR           0x40
+#define IC_IE           0x80
+
+/* the 16c552 is two 16c550s and a parallel port, so its definitions  
+ * of various register bits are OK for us.
+ */
+#include <linux/16c552.h>
+
+/* This is what the register layout looks like: */
+typedef struct 
+{
+   /* card registers */
+   u_char gap0;
+   volatile u_char dca_id;    /* (read) */
+#define dca_reset dca_id  /* (write) */
+   u_char gap1;
+   volatile u_char dca_ic;                        /* interrupt control */
+   u_char gap2;
+   volatile u_char dca_ocbrc;
+   u_char gap3;
+   volatile u_char dca_lcsm;
+   u_char gap4[8];
+   /* chip registers */
+   struct uart_16c550 uart;
+} hpdca_struct;
+
+/* purely because the IOEXT code I'm copying uses 5 :-> */
+#define MAX_HPDCA 5
+
+typedef struct 
+{
+   volatile struct uart_16c550 *uart;
+   int hasfifo;                                   /* does this UART have a FIFO? */
+   hpdca_struct *board;
+   int scode;                                     /* select code of this board */
+   int spurious_count;
+   int line;
+} hpdcaInfoType;
+
+extern int hpdca_num;                             /* number of detected boards */
+extern hpdcaInfoType hpdca_info[MAX_HPDCA];
+
+#define curruart(info) ((struct uart_16c550 *)(info->port))
+
+#define ser_DTRon(info)  curruart(info)->MCR |=  DTR
+#define ser_RTSon(info)  curruart(info)->MCR |=  RTS
+#define ser_DTRoff(info) curruart(info)->MCR &= ~DTR
+#define ser_RTSoff(info) curruart(info)->MCR &= ~RTS
+
+#endif /* ndef _SER_HPDCA_H_ */
diff -urN linux-2.2.18-pre15/drivers/char/ser_hypercom1.c linux/drivers/char/ser_hypercom1.c
--- linux-2.2.18-pre15/drivers/char/ser_hypercom1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_hypercom1.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,658 @@
+#define HYPERCOM1_VERSION 1
+#define HYPERCOM1_REV 13
+#define HYPERCOM1_DATE "8.7.99"
+/*
+ * HyperCOM1 driver for LinuxAPUS/M68K
+ * Project started 25-May-1999 by Gordon Huby
+ * Email: <gordon@ghuby.freeserve.co.uk>
+ * ---------------------------------------------------------------------------
+ * This code is based on ser_ioext.c by Jes Sorensen (jds@kom.auc.dk)
+ * and ser_whippet.c by Chris Sumner (chris@cpsumner.freeserve.co.uk)
+ * ---------------------------------------------------------------------------
+ * $Id: ser_hypercom1.c,v 1.13 1999/07/08 17:41:48 gordon Exp $
+ * ---------------------------------------------------------------------------
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file README.legal in the main directory of this archive
+ * for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+/* #include <linux/config.h> */
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/interrupt.h>
+#include <linux/m68kserial.h>
+
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/irq.h>
+
+#include "ser_hypercom1.h"
+
+/* comment out for no debuging */
+/* #define	HYPERCOM1_DEBUG	1 */
+
+/* port number */
+#ifndef SER_HYPERCOM1
+#define	SER_HYPERCOM1	113
+#endif
+
+/* Some usefull macros */
+#define ser_DTRon(uart)		((uart->MCR) |= MCR_DTR)
+#define ser_DTRoff(uart)	((uart->MCR) &= ~(MCR_DTR))
+#define ser_RTSon(uart)		((uart->MCR) |= MCR_RTS)
+#define ser_RTSoff(uart)	((uart->MCR) &= ~(MCR_RTS))
+
+/***************************** Prototypes *****************************/
+static void ser_init(struct m68k_async_struct *info);
+static void ser_deinit(struct m68k_async_struct *info,int leave_dtr);
+static void ser_enab_tx_int(struct m68k_async_struct *info,int enab_flag);
+static int  ser_check_custom_divisor(struct m68k_async_struct *info,int baud_base,int divisor);
+static void ser_change_speed(struct m68k_async_struct *info);
+static void ser_throttle(struct m68k_async_struct *info,int status);
+static void ser_break(struct m68k_async_struct *info,int break_flag);
+static void ser_get_serial_info(struct m68k_async_struct *info,struct serial_struct *retinfo);
+static unsigned int ser_get_modem_info(struct m68k_async_struct *info);
+static int ser_set_modem_info(struct m68k_async_struct *info,int new_dtr,int new_rts);
+static int ser_ioctl(struct tty_struct *tty,struct file *file,
+	struct m68k_async_struct *info,unsigned int cmd,unsigned long arg);
+static void ser_stop_receive(struct m68k_async_struct *info);
+static int ser_trans_empty(struct m68k_async_struct *info);
+/************************* End of Prototypes **************************/
+
+/*
+ * SERIALSWITCH structure for HyperCOM1 serial port
+ */
+
+static SERIALSWITCH hypercom1_ser_switch = {
+	ser_init,
+	ser_deinit,
+	ser_enab_tx_int,
+	ser_check_custom_divisor,
+	ser_change_speed,
+	ser_throttle,
+	ser_break,
+	ser_get_serial_info,
+	ser_get_modem_info,
+	ser_set_modem_info,
+	ser_ioctl,
+	ser_stop_receive,
+	ser_trans_empty,
+	NULL,
+};
+
+static u_int hypercom1_baud_table[20] = {
+	0,
+	50,
+	75,
+	110,
+	134,
+	150,
+	200,
+	300,
+	600,
+	1200,
+	1800,
+	2400,
+	4800,
+	9600,
+	19200,
+	38400,  /* The last of the standard rates. */
+	57600,  /* ASYNC_SPD_HI   */
+	115200, /* ASYNC_SPD_VHI  */
+	230400, /* ASYNC_SPD_SHI  */
+	480600, /* ASYNC_SPD_WARP */
+};
+
+/***************************************************************************/
+static const u_char fifo_trigger_level = TX_FIFO_TRIG_16 | RX_FIFO_TRIG_28; /* changeable */
+/*-------------------------------------------------------------------------*/
+static struct STARTECH_16C650 *hypercom1;
+static struct m68k_async_struct *amiga_info;
+static int line;
+/***************************************************************************/
+
+/*
+ * ser_interrupt()
+ */
+static void ser_interrupt(int irq, void *data, struct pt_regs *regs)
+{
+	struct m68k_async_struct *info = data;
+	u_char lsr,isr,ier;
+
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_interrupt()\n");
+#endif
+	isr = hypercom1->ISR;
+	ier = hypercom1->IER;
+
+	while (!(isr & IRQ_PEND)) {	/* loop until no more ints */
+
+		switch (isr & IRQ_MASK) {
+                    case IRQ_RLS:		/* Receiver Line Status */
+                    case IRQ_RDTO:		/* Receiver Data Timeout */
+                    case IRQ_RDR:		/* Receiver Data Ready */
+         /*
+	 * Copy chars to the tty-queue ...
+	 * Be careful that we aren't passing one of the
+	 * Receiver Line Status interrupt-conditions without noticing.
+	 */
+                    {
+                            int ch;
+                            
+                            lsr = hypercom1->LSR;
+                            while (lsr & LSR_RDR) {
+                                    u_char err;
+                                    ch = hypercom1->RHR;
+                                    
+                                    if (lsr & LSR_BI)	    err = TTY_BREAK;
+                                    else if (lsr & LSR_PE)  err = TTY_PARITY;
+                                    else if (lsr & LSR_ORE) err = TTY_OVERRUN;
+                                    else if (lsr & LSR_FE)  err = TTY_FRAME;
+                                    else err = 0;
+                                    
+                                    rs_receive_char(info, ch, err);
+                                    lsr = hypercom1->LSR;
+                            }
+                    }
+                    break;
+                    case IRQ_THRE:	/* Transmitter holding register empty */
+                    {
+                            int fifo_space = FIFO_SIZE;
+                            int ch;
+
+                            while (fifo_space > 0) {
+                                    fifo_space--;
+                                    if ((ch = rs_get_tx_char(info)) >= 0)        
+                                            hypercom1->THR = (u_char)ch;
+                                    if (ch == -1 || rs_no_more_tx(info)) {
+                                            ier &= ~(IER_THR);
+                                            break;
+                                    }
+                            }
+#ifdef HYPERCOM1_DEBUG
+                            printk("fifo_space=%d\n",fifo_space);
+#endif
+                    }
+                    break;
+                    case IRQ_MSR: /* Must be modem status register interrupt? */
+                    {
+                            u_char msr = hypercom1->MSR;
+
+                            if (info->flags & ASYNC_INITIALIZED) {
+                                    if (msr & MSR_DCTS) {
+#ifdef HYPERCOM1_DEBUG
+                                            printk("CTS = %s\n",(msr & MSR_CTS) ? "ON" : "OFF");
+#endif
+                                            rs_check_cts(info, (msr & MSR_CTS));
+                                    }
+                                    if (msr & MSR_DCD)
+                                            rs_dcd_changed(info, (msr & MSR_CD));
+                            }
+                    }
+                    break;
+                    
+		} /* switch (iir) */
+
+		isr = hypercom1->ISR;
+	} /* while IRQ_PEND */
+
+/* Re-enable UART interrupts */
+	hypercom1->IER = ier;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 
+ * ser_init() init the port as necessary, set RTS and DTR and enable interrupts
+ * It does not need to set the speed and other parameters, because change_speed,
+ * is called too.
+ */
+
+static void ser_init(struct m68k_async_struct *info)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_init()\n");
+#endif
+	/* Enable DTR and RTS */
+	hypercom1->MCR |= (MCR_DTR | MCR_RTS | MCR_OP2);
+
+       	hypercom1->FCR	= FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level;
+
+/* Enable interrupts. IF_EXTER irq has already been enabled in hypercom1_init()*/
+/* DON'T enable IER_THR here because there is nothing to send yet (murray)    */
+
+	hypercom1->IER |= (IER_RHR | IER_RLS | IER_MSI);
+
+	MOD_INC_USE_COUNT;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * deinit(): Stop and shutdown the port (e.g. disable interrupts, ...)
+ */
+static void ser_deinit(struct m68k_async_struct *info,int leave_dtr)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_deinit()\n");
+#endif
+	hypercom1->IER=0x00;
+        
+        hypercom1->FCR = FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level;
+
+	ser_RTSoff(hypercom1);
+	if (!leave_dtr)	ser_DTRoff(hypercom1);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   enab_tx_int(): Enable or disable the Tx Buffer Empty interrupt
+ *      independently from other interrupt sources. If the int is
+ *      enabled, the transmitter should also be restarted, i.e. if there
+ *      are any chars to be sent, they should be put into the Tx
+ *      register. The real en/disabling of the interrupt may be a no-op
+ *      if there is no way to do this or it is too complex. This Tx ints
+ *      are just disabled to save some interrupts if the transmitter is
+ *      stopped anyway. But the restarting must be implemented!
+ */
+static void ser_enab_tx_int(struct m68k_async_struct *info,int enab_flag)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_enab_tx_int(%s)\n",(enab_flag) ? "ON" : "OFF");
+#endif
+	if (enab_flag)	hypercom1->IER |= IER_THR;
+	else		hypercom1->IER &= ~(IER_THR);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   check_custom_divisor(): Check the given custom divisor for legality
+ *      and return 0 if OK, non-zero otherwise.
+ */
+static int ser_check_custom_divisor(struct m68k_async_struct *info,int baud_base,int divisor)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_check_custom_divisor()\n");
+#endif
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   change_speed(): Set port speed, character size, number of stop
+ *      bits and parity from the termios structure. If the user wants
+ *      to set the speed with a custom divisor, he is required to
+ *      check the baud_base first!
+ */
+static void ser_change_speed(struct m68k_async_struct *info)
+{
+	u_int cflag, real_baud, baud, chsize, stopb, parity, aflags;
+	u_int div = 0, ctrl = 0;
+
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_change_speed()\n");
+#endif
+	if (!info->tty || !info->tty->termios) return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)	info->flags |= ASYNC_CTS_FLOW;
+	else			info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)	info->flags &= ~ASYNC_CHECK_CD;
+	else			info->flags |= ASYNC_CHECK_CD;
+
+	if (baud & CBAUDEX)
+		baud = baud - (B57600 - B38400 -1);
+
+#ifdef HYPERCOM1_DEBUG
+	printk("Baud=%d\n",baud);
+#endif
+	if (baud == 15) {
+		switch (aflags) {
+		case ASYNC_SPD_HI:
+			baud += 1;
+			break;
+		case ASYNC_SPD_VHI:
+			baud += 2;
+			break;
+		case ASYNC_SPD_SHI:
+			baud += 3;
+			break;
+		case ASYNC_SPD_WARP:
+			baud += 4;
+			break;
+		case ASYNC_SPD_CUST:
+			div = info->custom_divisor;
+			break;
+		}
+	}
+	
+	if (!div) {
+		/* Maximum speed is 460800 */
+		if (baud > 19) baud = 19;
+		real_baud = hypercom1_baud_table[baud];
+		if (real_baud==0) div=0;
+		else div = HYPERCOM1_BAUD_BASE/real_baud;
+	}
+
+#ifdef HYPERCOM1_DEBUG
+	printk("divisor=%d, baud rate=%d\n",div,real_baud);
+#endif
+	if (!div) {
+		/* speed == 0 -> drop DTR */
+		ser_DTRoff(hypercom1);
+		return;
+	}
+
+/*
+ * We have to set DTR when a valid rate is chosen, otherwise DTR
+ * might get lost when programs use this sequence to clear the line:
+ *
+ * change_speed(baud = B0);
+ * sleep(1);
+ * change_speed(baud = Bx); x != 0
+ *
+ * The pc-guys do this aswell.
+ */
+	ser_DTRon(hypercom1);
+
+	if (chsize == CS8)	ctrl |= data_8bit;
+	else if (chsize == CS7)	ctrl |= data_7bit;
+	else if	(chsize == CS6)	ctrl |= data_6bit;
+	else if (chsize == CS5)	ctrl |= data_5bit;
+
+/* If stopb is true we set STB which means 2 stop-bits */
+/* otherwise we only get 1 stop-bit.                   */
+
+	ctrl |= (stopb ? LCR_SB : 0);
+
+	ctrl |= ((parity & PARENB) ? ((parity & PARODD) ? (LCR_PE) : (LCR_PE | LCR_EP)) : 0x00 );
+
+	hypercom1->LCR = (ctrl | LCR_DLE);
+
+	/* Store high byte of divisor */
+	hypercom1->DLM = ((div >> 8) & 0xff);
+
+#ifdef HYPERCOM1_DEBUG
+	printk("DLM = 0x%.2X\n",((div >> 8) & 0xff));
+#endif
+	/* Store low byte of divisor */
+	hypercom1->DLL = (div & 0xff);
+
+#ifdef HYPERCOM1_DEBUG
+	printk("DLL = 0x%.2X\n",(div & 0xff));
+#endif
+	hypercom1->LCR = ctrl;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   throttle(): Set or clear the RTS line according to 'status'.
+ */
+static void ser_throttle(struct m68k_async_struct *info,int status)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_throttle(rts=%s)\n", (status) ? "OFF" : "ON");
+#endif
+	if (status)	ser_RTSoff(hypercom1);
+	else		ser_RTSon(hypercom1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   set_break(): Set or clear the 'Send a Break' flag.
+ */
+static void ser_break(struct m68k_async_struct *info,int break_flag)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_set_break(%s)\n", (break_flag) ? "ON" : "OFF");
+#endif
+	if (break_flag)	hypercom1->LCR |= LCR_SETB;
+	else		hypercom1->LCR &= ~LCR_SETB;
+
+	return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   get_serial_info(): Fill in the baud_base and custom_divisor
+ *      fields of a serial_struct. It may also modify other fields, if
+ *      needed.
+ */
+static void ser_get_serial_info(struct m68k_async_struct *info,struct serial_struct *retinfo)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_get_serial_info()\n");
+#endif	
+	retinfo->baud_base = HYPERCOM1_BAUD_BASE;
+	retinfo->xmit_fifo_size = FIFO_SIZE;	/* This field is currently ignored, */
+						/* by the upper layers of the       */
+						/* serial-driver.                   */
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   get_modem_info(): Return the status of RTS, DTR, DCD, RI, DSR and CTS.
+ */
+static unsigned int ser_get_modem_info(struct m68k_async_struct *info)
+{
+	u_char msr, mcr;
+
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_get_modem()\n");
+#endif
+
+	msr = hypercom1->MSR;
+	mcr = hypercom1->MCR;	/* The DTR and RTS are located in the */
+				/* ModemControlRegister ...           */
+	return (
+		((mcr & MCR_DTR) ? TIOCM_DTR : 0) |
+		((mcr & MCR_RTS) ? TIOCM_RTS : 0) |
+
+		((msr & MSR_CD) ? TIOCM_CAR : 0) |
+		((msr & MSR_CTS) ? TIOCM_CTS : 0) |
+		((msr & MSR_DSR) ? TIOCM_DSR : 0) |
+		((msr & MSR_RING) ? TIOCM_RNG : 0)
+		);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   set_modem_info(): Set the status of RTS and DTR according to
+ *      'new_dtr' and 'new_rts', resp. 0 = clear, 1 = set, -1 = don't change
+ */
+static int ser_set_modem_info(struct m68k_async_struct *info,int new_dtr,int new_rts)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_set_modem(dtr=%s, rts=%s)\n",(new_dtr == 0) ? "OFF" : "ON",(new_rts == 0) ? "OFF" : "ON");
+#endif
+
+	if (new_dtr == 0)	ser_DTRoff(hypercom1);
+	else if (new_dtr == 1)	ser_DTRon(hypercom1);
+
+	if (new_rts == 0)	ser_RTSoff(hypercom1);
+	else if (new_rts == 1)	ser_RTSon(hypercom1);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   ioctl(): Process any port-specific ioctl's. This pointer may be
+ *      NULL, if the port has no own ioctl's.
+ */
+static int ser_ioctl(struct tty_struct *tty,struct file *file,
+	struct m68k_async_struct *info,unsigned int cmd,unsigned long arg)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_ioctl()\n");
+#endif
+	return -ENOIOCTLCMD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   stop_receive(): Turn off the Rx part of the port, so no more characters
+ *      will be received. This is called before shutting the port down.
+ */
+static void ser_stop_receive(struct m68k_async_struct *info)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_stop_receive()\n");
+#endif
+	/* Disable uart receive and status interrupts */
+	hypercom1->IER &= ~(IER_RHR | IER_RLS | IER_MSI);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *   trans_empty(): Return !=0 if there are no more characters still to be
+ *      sent out (Tx buffer register and FIFOs empty)
+ */
+static int ser_trans_empty(struct m68k_async_struct *info)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_trans_empty()\n");
+#endif
+	return (hypercom1->LSR & LSR_THE);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ser_reset_port(void)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("ser_reset_port()\n");
+#endif
+	hypercom1->IER=0x00;	/* disable interrupts */
+	hypercom1->MCR=MCR_OP2;	/* master int */
+
+	(void)hypercom1->ISR;
+	(void)hypercom1->LSR;
+	(void)hypercom1->MSR;
+
+	hypercom1->LCR=LCR_DLE;
+	hypercom1->DLL=0x30;
+	hypercom1->DLM=0x00;
+
+	hypercom1->LCR=0x03;	/* 8Bits, no parity, 1 stop bits, and disable latch enable */
+	hypercom1->FCR=FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int check_port(void)
+{
+	int port_found=0;
+
+	/* A write to SCRATCH with any byte, selects hypercom3/3+ first uart. */
+	hypercom1->SCR=0xaa;
+
+	if (hypercom1->LSR & LSR_TE)
+		  port_found=1;
+
+	return port_found;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int hypercom1_init(void)
+{
+	unsigned long flags;
+	struct serial_struct req;
+	struct m68k_async_struct *info;
+	
+#ifdef HYPERCOM1_DEBUG
+	printk("hypercom1_init\n");
+#endif
+        if (!(MACH_IS_AMIGA))
+                return -ENODEV;
+
+	/* Check if amiga1200 */
+	if (amiga_model != AMI_1200)
+		return -ENODEV;
+
+	/* init hypercom1 pointer */
+	hypercom1 = (struct STARTECH_16C650 *) (zTwoBase + HYPERCOM1_PHYSADDR);
+
+	/* Check if port exists */
+	printk("Probing for HyperCOM1, version %d.%d date %s\n",HYPERCOM1_VERSION,HYPERCOM1_REV,HYPERCOM1_DATE); 
+	if (check_port() == 0) {
+		printk("HyperCOM1 A1200 serial port not found\n");
+		return -ENODEV;
+	}
+        printk("Found HyperCOM1 A1200 serial port\n");
+        
+	ser_reset_port();
+
+	req.line = -1;
+	req.type = SER_HYPERCOM1;
+	req.port = (int) &(hypercom1->RHR);
+
+	if ((line = register_serial( &req )) < 0) {
+		printk("Cannot register Hypercom1 serial port: no free device\n");
+		return -EBUSY;
+	}
+	info = &rs_table[line];
+	info->nr_uarts = 1;
+	info->sw = &hypercom1_ser_switch;
+
+	amiga_info = info; /* static variable */
+
+	save_flags(flags);
+	cli();
+
+	/* Install handler for EXTER interupts */
+	request_irq(IRQ_AMIGA_EXTER, ser_interrupt, 0, "hypercom1 serial port", info);
+
+	restore_flags(flags);
+	return 0;
+}
+
+/************************* Module Functions ********************************/
+
+#ifdef MODULE
+int init_module(void)
+{
+	return hypercom1_init();
+}
+
+void cleanup_module(void)
+{
+#ifdef HYPERCOM1_DEBUG
+	printk("Closing HyperCOM1 Device!\n");
+#endif
+	unregister_serial(line);
+
+	ser_reset_port();
+
+	free_irq(IRQ_AMIGA_EXTER,amiga_info);
+}
+#endif
diff -urN linux-2.2.18-pre15/drivers/char/ser_hypercom1.h linux/drivers/char/ser_hypercom1.h
--- linux-2.2.18-pre15/drivers/char/ser_hypercom1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_hypercom1.h	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,172 @@
+/*
+ * Serial driver for VMC's HyperCOM1 Serial Card for the Amiga1200.
+ * Copyright Gordon Huby 25-May-1999
+ * Email: <gordon@ghuby.freeserve.co.uk>
+ * ---------------------------------------------------------------------------
+ * Hypercom1 == StarTech 16c650 UART
+ * ---------------------------------------------------------------------------
+ * $Id: ser_hypercom1.h,v 1.12 1999/07/08 18:51:03 gordon Exp $
+ * ---------------------------------------------------------------------------
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file README.legal in the main directory of this archive
+ * for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+#ifndef	_SER_HYPERCOM1_H_
+#define	_SER_HYPERCOM1_H_
+
+#define	HYPERCOM1_CLOCK_RATE	(7372800)
+#define	HYPERCOM1_BAUD_BASE	(HYPERCOM1_CLOCK_RATE/16)
+#define	HYPERCOM1_PHYSADDR	(0xD80021)
+
+#define FIFO_SIZE		(32)
+
+#ifndef PAD_S
+#define PAD_S			3
+#endif
+
+struct STARTECH_16C650 {
+	u_char	RHR;		/* Reciever Holding Register */
+	u_char	pad0[PAD_S];
+	u_char	IER;		/* Interupt Enable Register */
+	u_char	pad1[PAD_S];
+	u_char	FCR;		/* FIFO control register */
+	u_char	pad2[PAD_S];
+	u_char	LCR;		/* Line control register */
+	u_char	pad3[PAD_S];
+	u_char	MCR;		/* Modem Control Register */
+	u_char	pad4[PAD_S];
+	u_char	LSR;		/* Line Status Register */
+	u_char	pad5[PAD_S];
+	u_char	MSR;		/* Modem Status Register */
+	u_char	pad6[PAD_S];
+	u_char	SCR;		/* Scratchpad Register */
+};
+
+#define	THR	RHR		/* Transmit Holding Register */
+#define	ISR	FCR		/* Interrupt Status Register */
+
+#define	LSB	RHR		/* Divisor latch lower address. Enabled only when bit 7 of MCR = 1 */
+#define	MSB	IER		/* Divisor latch middle address. Enabled only when bit 7 of MC1 = 1 */
+
+#define DLL	LSB
+#define DLM	MSB
+
+#define EFR	RHR		/* Enhanced Feature Register Set, Enabled only when LCR is set to 0xBF */
+#define XON1	IER
+#define XON2	FCR
+#define XOFF1	LCR
+#define XOFF2	MCR
+
+/*
+ * BIT DEFS FOR ST 16c650
+ */
+
+/* Interrupt Enable Register */		/* From Page 22 of st16c650.pdf */
+#define	IER_RHR		(1<<0)		/* Recieve Holding Register */
+#define	IER_THR		(1<<1)		/* Transmit Holding Register */
+#define	IER_RLS		(1<<2)		/* Recieve Line Status Interrupt */
+#define	IER_MSI		(1<<3)		/* Modem Status Interrupt */
+#define	IER_SM		(1<<4)		/* Sleep Mode */
+#define	IER_XOI		(1<<5)		/* Xoff Interrupt */
+#define	IER_RTS		(1<<6)		/* RTS Interrupt */
+#define	IER_CTS		(1<<7)		/* CTS Interupt */
+
+/* Fifo control register */
+#define	FCR_FE			(1<<0)		/* Fifo Enable */
+#define	FCR_RFR			(1<<1)		/* Recieve Fifo reset */
+#define	FCR_XFR			(1<<2)		/* XMIT Fifo Reset */
+#define	FCR_DMS			(1<<3)		/* DMA Mode Select */
+#define	FCR_TX_TRIG_LSB		(1<<4)		/* TX Trigger LSB */
+#define	FCR_TX_TRIG_MSB		(1<<5)		/* TX Trigger MSB */
+#define	FCR_RX_TRIG_LSB	        (1<<6)		/* RX Trigger LSB */
+#define	FCR_RX_TRIG_MSB	        (1<<7)		/* RX Trigger MSB */
+
+#define	TX_FIFO_TRIG_16		(0x00)
+#define	TX_FIFO_TRIG_8		(FCR_TX_TRIG_LSB)
+#define	TX_FIFO_TRIG_24		(FCR_TX_TRIG_MSB)
+#define	TX_FIFO_TRIG_30		(FCR_TX_TRIG_LSB | FCR_TX_TRIG_MSB)
+
+#define	RX_FIFO_TRIG_8	        (0x00)
+#define	RX_FIFO_TRIG_16	        (FCR_RX_TRIG_LSB)
+#define	RX_FIFO_TRIG_24	        (FCR_RX_TRIG_MSB)
+#define	RX_FIFO_TRIG_28	        (FCR_RX_TRIG_LSB | FCR_RX_TRIG_MSB)
+
+/* Interupt Status Register */
+#define	ISR_IS		(1<<0)		/* INT status */
+#define	ISR_IPB0	(1<<1)		/* INT priority bit 0 */
+#define	ISR_IPB1	(1<<2)		/* INT priority bit 1 */
+#define	ISR_IPB2	(1<<3)		/* INT prioroty bit 2 */
+#define	ISR_IPB3	(1<<4)		/* INT priority bit 3 */
+#define	ISR_IPB4	(1<<5)		/* INT priority bit 4 */
+#define	ISR_FE1		(1<<6)		/* Fifo's enabled */
+#define	ISR_FE2		(1<<7)		/* Fifo's enabled */
+
+#define	IRQ_PEND	ISR_IS		/* Logic 0 = pending int, Logic 1 = no pending int */
+#define	IRQ_RLS		(0x06)		/* Reciever line status register */
+#define	IRQ_RDR		(0x04)		/* Recieved Data Ready */
+#define	IRQ_RDTO	(0x0C)		/* Recieve Data time out */
+#define	IRQ_THRE	(0x02)		/* Transmitter Holding Register Empty */
+#define	IRQ_MSR		(0x00)		/* Modem Status Register */
+#define	IRQ_RXOFF	(0x10)		/* (Recieved Xoff signal) / Special character */
+#define	IRQ_CTSRTS	(0x20)		/* CTS, RTS change of state */
+
+#define	IRQ_MASK	(ISR_IPB0 | ISR_IPB1 | ISR_IPB2 | ISR_IPB3 | ISR_IPB4)
+
+/* Line Control Register */
+#define	LCR_WLB0	(1<<0)		/* Word Length Bit 0 */
+#define	LCR_WLB1	(1<<1)		/* Word Length Bit 1 */
+#define	LCR_SB		(1<<2)		/* Stop Bits */
+#define	LCR_PE		(1<<3)		/* Parity enable */
+#define	LCR_EP		(1<<4)		/* Even Parity */
+#define	LCR_SP		(1<<5)		/* Set Parity */
+#define	LCR_SETB	(1<<6)		/* Set Break */
+#define	LCR_DLE		(1<<7)		/* Divisor Latch Enable */
+
+#define	data_5bit	(0x00)
+#define	data_6bit	(0x01)
+#define	data_7bit	(0x02)
+#define	data_8bit	(0x03)
+
+/* Modem Control Register */
+#define	MCR_DTR		(1<<0)		/* DTR */
+#define	MCR_RTS		(1<<1)		/* RTS */
+#define MCR_OP1		(1<<2)		/* OP1 */
+#define MCR_OP2		(1<<3)		/* OP1 / IRQx enable  */
+#define MCR_LB		(1<<4)		/* Loop Back */
+#define MCR_ITS		(1<<5)		/* INT type select */
+#define MCR_IRE		(1<<6)		/* IR enable */
+#define MCR_CS		(1<<7)		/* Clock Select. logic 0=normal logic 1=4*baudrate*/
+
+/* Line Status Register */
+#define	LSR_RDR		(1<<0)		/* Recieve Data Ready */
+#define	LSR_ORE		(1<<1)		/* OverRun Error */
+#define	LSR_PE		(1<<2)		/* Parity Error */
+#define	LSR_FE		(1<<3)		/* Framing Error */
+#define	LSR_BI		(1<<4)		/* Break Interupt */
+#define	LSR_THE		(1<<5)		/* Trans Holding Empty */
+#define	LSR_TE		(1<<6)		/* Trans Empty */
+#define	LSR_FDE		(1<<7)		/* Fifo Data Error */
+
+/* Modem Status Register */
+#define	MSR_DCTS	(1<<0)		/* Delta CTS */
+#define	MSR_DDSR	(1<<1)		/* Delta DSR */
+#define	MSR_DRI		(1<<2)		/* Delta RI */
+#define MSR_DCD		(1<<3)		/* Delta CD */
+#define MSR_CTS		(1<<4)		/* CTS */
+#define MSR_DSR		(1<<5)		/* DSR */
+#define MSR_RING	(1<<6)		/* RI */
+#define MSR_CD		(1<<7)	        /* CD */
+
+/* Enhanced Feature Register */
+#define EFR_CONT0       (1<<0)          /* Cont0 TX RX Control */
+#define EFR_CONT1       (1<<1)          /* Cont1 TX RX Control */
+#define EFR_CONT2       (1<<2)          /* Cont2 TX RX Control */
+#define EFR_CONT3       (1<<3)          /* Cont3 TX RX Control */
+#define EFR_ENA         (1<<4)          /* Enable IER bits 4-7, ISR FCR bits 4-5, MCR bits 5-7 */ 
+#define EFR_SCS         (1<<5)          /* Special Char Select */
+#define EFR_AUTORTS     (1<<6)          /* Auto RTS */
+#define EFR_AUTOCTS     (1<<7)          /* Auto CTS */
+
+#endif	/* _SER_HYPERCOM1_H_ */
diff -urN linux-2.2.18-pre15/drivers/char/ser_mfc.c linux/drivers/char/ser_mfc.c
--- linux-2.2.18-pre15/drivers/char/ser_mfc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_mfc.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,726 @@
+
+/* serial driver for the two serial ports of a Multiface Card III
+ *
+ * Might also work with a SerialMaster or Multiface II, as far as I know
+ * the only difference is the frequency of the used quartz.
+ * Test it, and let me know if it works. Changing the baud table isn't worth a
+ * whole new driver.
+ * (To hardware hackers: It should be possible to get 115200 Baud with the older 
+ * card by replacing the 3.6864 MHz quartz with a 7.3728 MHz one. But beware!
+ * The only one who knows that now effectively all baudrates are doubled is you
+ * and not any application. Besides, you should know what you are doing and you
+ * do it at your own risk.)
+ *
+ * Due to hardware constraints I decided to make the two ports of the card different.
+ * Port0 (the 9-pin-one) is only able to handle the following baud rates:
+ * 150, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400 
+ * (MFC II and Serial Master can do all standard rates.)
+ * Port1 (the 25-pin-one) can handle any approbriate custom baud rate.
+ * Tell me if you would prefer it the other way and why. Perhaps I'll make it
+ * configurable.
+ * 
+ * Another solution would be to handle both ports the same way.
+ * But that would mean to support only standard baud rates or to get into
+ * big difficulties. On the other hand, you have more standard rates available.
+ *
+ * MIDI is missed with 5%. Can anyone tell me how to figure out which quartz
+ * is attached by software? I think the duart.device simply assumes 4.000MHz
+ * when you select MIDI baudrate.
+ *
+ * Another Hardware lack: If you select 5 bit character size a halve stop bit 
+ * is sent too much.
+ *
+ * Hint to BSC: If they had used the IP2/3 pins for DCD and IP4/5 for DSR,
+ * I could use the change state interrupt feature of the duart instead of polling
+ * the DCD every vblank-interrupt. (Or does someone want to be interrupted when
+ * DSR changes state (i.e. modem switched on/off) instead of knowing when a 
+ * connection is established/lost?)
+ *
+ * Note: The duart is able to handle the RTS/CTS-protocol in hardware.
+ * I use this feature to prevent receiver overruns by letting the DUART drop RTS
+ * when the FIFO is full
+ *
+ * created 15.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ * adapted 11.12.95 for 1.2.13 Joerg Dorchain
+ *
+ * improved 6. 2.96 Joerg Dorchain
+ *  -  fixed restart bug
+ *
+ */
+
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/m68kserial.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+
+#include "multiface.h"
+#include "mc68681.h"
+
+/* how many cards are to be supported */
+#define MAX_CARD 5
+
+/***************************** Prototypes *****************************/
+
+static void mfc_interrupt(int irq, void *data, struct pt_regs *fp);
+static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp);
+static void mfc_init( struct m68k_async_struct *info );
+static void mfc_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void mfc_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int  mfc_check_custom_divisor( struct m68k_async_struct *info, int
+				     baud_base, int divisor );
+static void mfc_change_speed( struct m68k_async_struct *info );
+static void mfc_throttle( struct m68k_async_struct *info, int status );
+static void mfc_set_break( struct m68k_async_struct *info, int break_flag );
+static void mfc_get_serial_info( struct m68k_async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int mfc_get_modem_info( struct m68k_async_struct *info );
+static int mfc_set_modem_info( struct m68k_async_struct *info, int new_dtr,
+			      int new_rts );
+static void mfc_stop_receive(struct m68k_async_struct *info);
+static int mfc_trans_empty(struct m68k_async_struct *info);
+
+/************************* End of Prototypes **************************/
+
+
+
+/* SERIALSWITCH structure for the Multiface serial ports */
+
+static SERIALSWITCH mfc_ser_switch = {
+    mfc_init, mfc_deinit, mfc_enab_tx_int,
+    mfc_check_custom_divisor, mfc_change_speed,
+    mfc_throttle, mfc_set_break,
+    mfc_get_serial_info, mfc_get_modem_info,
+    mfc_set_modem_info, NULL,
+    mfc_stop_receive, mfc_trans_empty, NULL
+};
+
+
+
+/* These value go into the csr (Clock Select Registers) */
+/* 0xff means not available, B0 is handle special anyway */
+ static u_char fixed_baud_III[16] = {
+ 0, /* B0 */
+ 0xff, /* B50 */
+ 0xff, /* B75 */
+ 0xff, /* B110 */
+ 0xff, /* B134 */
+ 0xff, /* B150 */
+ 0xff, /* B200 */
+ 0x33, /* B300 */
+ 0x44, /* B600 */
+ 0x55, /* B1200 */
+ 0xff, /* B1800 */
+ 0x66, /* B2400 */
+ 0x88, /* B4800 */
+ 0x99, /* B9600 */
+ 0xbb, /* B19200 */
+ 0xcc  /* B38400 */
+ };
+/* These are precalculated timer values for standart rates */
+ static u_short custom_baud_III[18] = {
+ 0, /* B0 */
+ 4608, /* B50 */
+ 3072, /* B75 */
+ 2095, /* B110 - a little bit off (0.02%) */
+ 1713, /* B134 - off by 0.0006% */
+ 1536, /* B150 */
+ 1152, /* B200 */
+ 768, /* B300 */
+ 384, /* B600 */
+ 192, /* B1200 */
+ 128, /* B1800 */
+ 96, /* B2400 */
+ 48, /* B4800 */
+ 24, /* B9600 */
+ 12, /* B19200 */
+ 6, /* B38400 */
+ 4, /* B57600 */
+ 2 /* B115200 */
+ };
+
+static int nr_mfc; /* nr of ports configured */
+static int lines[MAX_CARD * 2]; /* accociated tty lines (index in rs_table) */
+static unsigned int board_index[MAX_CARD]; /* nr. of zorro slot */
+static unsigned char imask[MAX_CARD];
+static unsigned char acmask[MAX_CARD];
+
+static void mfc_fill_tx(struct m68k_async_struct *info)
+{
+struct duart *dp;
+struct duarthalf *d;
+int sta,i,mask,ch;
+
+dp=info->board_base;
+d=(struct duarthalf *)info->port;
+i=(info->nr_uarts-1)/2;
+mask=(((info->nr_uarts-1)%2)==0)?1:16;
+sta=d->sr_csr.sr;
+while ((sta & 4) != 0) {   /* fill the buffer */
+  if ((ch = rs_get_tx_char( info )) >= 0)
+    d->hr.thr = ch;
+  else
+    break;
+  sta=d->sr_csr.sr;
+}
+if (rs_no_more_tx( info ))
+  imask[i] &= ~mask;
+else
+  imask[i] |= mask;
+dp->ir.imr = imask[i];
+}
+
+static u_char isr_entered = 0;
+
+static void mfc_interrupt(int irq, void *data, struct pt_regs *fp)
+{
+int i;
+int ireq;
+struct duart *dp;
+int ch;
+int status;
+int err;
+int ipch;
+u_short intenar;
+
+
+intenar = (custom.intenar & IF_EXTER);
+custom.intena = IF_EXTER;
+if (isr_entered) {
+  printk("Reentering MFC isr.\n");
+  custom.intena = (intenar | IF_SETCLR);
+  return;
+}
+isr_entered = 1;
+
+custom.intena = (intenar | IF_SETCLR);
+
+for (i = 0; i < nr_mfc; i += 2) {
+  dp = rs_table[lines[i]].board_base;
+  ireq = dp->ir.isr;
+  if ((ireq & imask[i/2]) !=0) { /* if it is "our" interrupt */
+    /* first the transmitter */
+    mfc_fill_tx(rs_table + lines[i]);      /* channel A */
+    mfc_fill_tx(rs_table + lines[i + 1]);  /* channel B */
+    { /* test for receiver ready */
+      {
+	status = dp->pa.sr_csr.sr; /* empty the FIFO */
+	while ((status & 1) != 0) {
+	  ch = dp->pa.hr.rhr;
+	  err = 0;
+	  if ((status & SR_BREAK) != 0)
+	    err |= TTY_BREAK;
+	  if ((status & SR_FRAMING) != 0)
+	    err |= TTY_FRAME;
+	  if ((status & SR_PARITY) != 0)
+	    err |= TTY_PARITY;
+	  if ((status & SR_OVERRUN) != 0)
+	    err |= TTY_OVERRUN;
+	  rs_receive_char(rs_table + lines[i], ch, err);
+	  status = dp->pa.sr_csr.sr;
+	} 
+      }
+      {
+	status = dp->pb.sr_csr.sr;
+	while (( status & 1) != 0) {
+	  ch = dp->pb.hr.rhr;
+	  err = 0;
+	  if ((status & SR_BREAK) != 0)
+	    err |= TTY_BREAK;
+	  if ((status & SR_FRAMING) != 0)
+	    err |= TTY_FRAME;
+	  if ((status & SR_PARITY) != 0)
+	    err |= TTY_PARITY;
+	  if ((status & SR_OVERRUN) != 0)
+	    err |= TTY_OVERRUN;
+	  rs_receive_char(rs_table + lines[i + 1], ch, err);
+	  status = dp->pb.sr_csr.sr;
+	} 
+      }
+    }
+    { /* CTS changed */
+     ipch = dp->ipcr_acr.ipcr;
+     if ((ipch & 16) !=0)  /* port a */
+       rs_check_cts(rs_table + lines[i], !(ipch & 1)); 
+     if ((ipch & 32) !=0)
+       rs_check_cts(rs_table + lines[i + 1], !(ipch & 2));
+    }
+  }
+}
+custom.intreq = IF_EXTER;
+isr_entered = 0;
+}
+
+static u_char curr_dcd[MAX_CARD];
+
+static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp)
+{
+int i;
+struct duart *dp;
+u_char bits;
+
+for (i = 0; i < nr_mfc; i += 2) {
+  if (rs_table[lines[i]].flags & ASYNC_INITIALIZED) {
+    dp = rs_table[lines[i]].board_base;
+    bits = dp->ipr_opcr.ipr & 48;
+    if (bits ^ curr_dcd[i/2]) {
+      if (((bits ^ curr_dcd[i/2]) & 16) != 0)
+	rs_dcd_changed(rs_table + lines[i], !(bits & 16));
+      if (((bits ^ curr_dcd[i/2]) & 32) != 0)
+	rs_dcd_changed(rs_table + lines[i + 1], !(bits & 32));
+    }
+    curr_dcd[i/2] = bits;
+  }
+}
+}
+
+static void mfc_init( struct m68k_async_struct *info)
+{
+struct duart *dp;
+struct duarthalf *d;
+
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+if (((info->nr_uarts-1)%2) == 0) {  /* port A */
+  dp->start_sopc.sopc = 5;		/* DTR & RTS on */
+  imask[(info->nr_uarts-1)/2] |= 3|128; /* interrupts on */
+  acmask[(info->nr_uarts-1)/2] |= 1;
+}
+else {
+  dp->start_sopc.sopc = 10;
+  imask[(info->nr_uarts-1)/2] |= 48|128;
+  acmask[(info->nr_uarts-1)/2] |= 2;
+}
+d->cr = CR_RESET_RX;
+d->cr = CR_RESET_TX;
+d->cr = CR_RESET_ERR;
+d->cr = CR_RESET_BREAK;
+d->cr = CR_STOP_BREAK;
+d->cr = CR_RX_ON;
+d->cr = CR_TX_ON;
+curr_dcd[(info->nr_uarts-1)/2] = dp->ipr_opcr.ipr & 48;
+custom.intena = IF_EXTER;
+/* enable interrupts */
+dp->ir.imr = imask[(info->nr_uarts-1)/2];
+dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2];
+custom.intena = (IF_EXTER | IF_SETCLR);
+MOD_INC_USE_COUNT;
+}
+
+static void mfc_deinit( struct m68k_async_struct *info, int leave_dtr)
+{
+struct duart *dp;
+struct duarthalf *d;
+u_short intenar;
+
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+/* turn off interrupts CTS and DTR if required */
+if (((info->nr_uarts-1)%2) == 0) {
+  dp->stop_ropc.ropc = 1;
+  if (!leave_dtr)
+    dp->stop_ropc.ropc = 4;
+  imask[(info->nr_uarts-1)/2] &= ~3;
+  acmask[(info->nr_uarts-1)/2] &= ~1;
+}
+else {
+  dp->stop_ropc.ropc = 2;
+  if (!leave_dtr)
+    dp->stop_ropc.ropc = 8;
+  imask[(info->nr_uarts-1)/2] &= ~48;
+  acmask[(info->nr_uarts-1)/2] &= ~2;
+}
+intenar = (custom.intenar & IF_EXTER);
+custom.intena = IF_EXTER;
+dp->ir.imr = imask[(info->nr_uarts-1)/2];
+dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2];
+custom.intena = (intenar | IF_SETCLR);
+/* disable transmitter and receiver after current character */
+d->cr = CR_RX_OFF;
+d->cr = CR_TX_OFF;
+MOD_DEC_USE_COUNT;
+}
+
+static void mfc_enab_tx_int(struct m68k_async_struct *info, int enab_flag)
+{
+struct duart *dp;
+
+if (enab_flag)
+  mfc_fill_tx(info); /* fills the tx buf and enables interrupts */
+else { /* disable interrupts */
+  /* we could also disable the transmitter at all */
+  dp = info->board_base;
+  if (((info->nr_uarts-1)%2) == 0) 
+    imask[(info->nr_uarts-1)/2] &= ~1;
+  else
+    imask[(info->nr_uarts-1)/2] &= ~16;
+  dp->ir.imr=imask[(info->nr_uarts-1)/2];
+}
+}
+
+static int mfc_check_custom_divisor(struct m68k_async_struct *info,
+      int baud_base, int divisor)
+{
+if (((info->nr_uarts-1)%2) == 0) 
+  return 1;
+if (baud_base != 230400) /* change for MFC II */
+  return 1;
+if (divisor < 2)
+  return 1;
+return 0;
+}
+
+static void mfc_change_speed(struct m68k_async_struct *info)
+{
+u_int cflag, baud, chsize, stopb, parity, aflags;
+u_int ctrl = 0;
+u_short div = 0;
+struct duart *dp;
+struct duarthalf *d;
+int dummy;
+
+if (!info->tty || ! info->tty->termios)
+  return;
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+cflag = info->tty->termios->c_cflag;
+baud = cflag & CBAUD;
+chsize = cflag & CSIZE;
+stopb = cflag & CSTOPB;
+parity = cflag & (PARENB | PARODD);
+aflags = info->flags & ASYNC_SPD_MASK;
+
+if (cflag & CRTSCTS)
+  info->flags |= ASYNC_CTS_FLOW;
+else
+  info->flags &= ~ASYNC_CTS_FLOW;
+if (cflag & CLOCAL)
+  info->flags &= ~ASYNC_CHECK_CD;
+else
+  info->flags |= ASYNC_CHECK_CD;
+
+if (baud & CBAUDEX) {
+  baud &= ~CBAUDEX;
+  if (baud < 1 || baud > 4)
+    info->tty->termios->c_cflag &= ~CBAUDEX;
+  else
+    baud += 15;
+}
+
+if (baud == 15) {
+  switch (aflags) {
+  case ASYNC_SPD_HI:
+    baud += 1;
+    break;
+  case ASYNC_SPD_VHI:
+    baud += 2;
+    break;
+  case ASYNC_SPD_SHI:
+    baud += 3;
+    break;
+  case ASYNC_SPD_WARP:
+    baud += 4;
+    break;
+  case ASYNC_SPD_CUST:
+    div = info->custom_divisor;
+    break;
+  }
+}
+if (!div) {
+  if (baud > 17)
+    baud = 17;
+  div = custom_baud_III[baud];
+}
+
+if (!div) { /* drop DTR */
+  if (((info->nr_uarts-1)%2) == 0)
+    dp->stop_ropc.ropc = 4;
+  else
+    dp->stop_ropc.ropc = 8;
+  return;
+}
+
+if (((info->nr_uarts-1)%2) == 0) {
+  dp->start_sopc.sopc = 4; /* set DTR */
+  if (baud > 15)
+    baud =15;
+  if (fixed_baud_III[baud] != 0xff)
+    d->sr_csr.csr = fixed_baud_III[baud];
+  else
+    printk("ttyS%d: ignored illegal baudrate %d\n", info->line,baud);
+}
+else {
+  dp->start_sopc.sopc = 8;
+  dp->ctu = div / 256;
+  dp->ctl = div % 256;
+  dummy = dp->start_sopc.start;
+}
+d->mr.mr2 = (stopb) ? MR2_2STOP: MR2_1STOP;
+
+if (chsize == CS8)
+  ctrl |= MR1_8BITS;
+else if (chsize == CS7)
+  ctrl |= MR1_7BITS;
+else if (chsize == CS6)
+  ctrl |= MR1_6BITS;
+else if (chsize == CS5)
+  ctrl |= MR1_5BITS;
+
+if (parity & PARENB)
+  ctrl |= MR1_PARITY_WITH;
+else
+  ctrl |= MR1_PARITY_NO;
+if (parity & PARODD)
+  ctrl |= MR1_PARITY_ODD;
+
+d->cr = CR_RESET_MR;
+d->mr.mr1 = ctrl|MR1_RxRTS_ON;
+}
+
+static void mfc_throttle (struct m68k_async_struct *info, int status)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0) {
+  if (status)
+    dp->stop_ropc.ropc = 1;
+  else 
+    dp->start_sopc.sopc = 1;
+}
+else {
+  if (status)
+    dp->stop_ropc.ropc = 2;
+  else 
+    dp->start_sopc.sopc = 2;
+}
+}
+
+static void mfc_set_break (struct m68k_async_struct *info, int break_flag)
+{
+struct duarthalf *d;
+
+d = (struct duarthalf *)info->port;
+if (break_flag)
+  d->cr = CR_START_BREAK;
+else
+  d->cr = CR_STOP_BREAK;
+}
+
+static void mfc_get_serial_info( struct m68k_async_struct *info, 
+      struct serial_struct *retinfo)
+{
+if (((info->nr_uarts-1)%2) == 0) {
+  retinfo->baud_base = 0;
+  retinfo->custom_divisor = 0;
+}
+else {
+  retinfo->baud_base = 230400;
+  retinfo->custom_divisor = info->custom_divisor;
+}
+}
+
+static unsigned int mfc_get_modem_info(struct m68k_async_struct *info)
+{
+struct duart *dp;
+u_char inf;
+
+dp = info->board_base;
+inf = dp->ipr_opcr.ipr;
+if (((info->nr_uarts-1)%2) == 0) 
+  return ((inf & 1) ? 0 : TIOCM_CTS) |
+         ((inf & 4) ? 0 : TIOCM_DSR) |
+         ((inf & 16) ? 0 : TIOCM_CAR) |
+         ((dp->pa.ri & 1) ? 0 : TIOCM_RNG);
+else
+  return ((inf & 2) ? 0 : TIOCM_CTS) |
+         ((inf & 8) ? 0 : TIOCM_DSR) |
+         ((inf & 32) ? 0 : TIOCM_CAR) |
+         ((dp->pb.ri & 2) ? 0 : TIOCM_RNG);
+/* No chance to check DTR & RTS easily */
+}
+
+static int mfc_set_modem_info(struct m68k_async_struct *info, int new_dtr, int new_rts)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0) {
+  if (new_dtr == 0)
+    dp->stop_ropc.ropc = 4;
+  else if (new_dtr == 1)
+    dp->start_sopc.sopc = 4;
+  if (new_rts == 0)
+    dp->stop_ropc.ropc = 1;
+  else if (new_rts == 1)
+    dp->start_sopc.sopc = 1;
+}
+else {
+  if (new_dtr == 0)
+    dp->stop_ropc.ropc = 8;
+  else if (new_dtr == 1)
+    dp->start_sopc.sopc = 8;
+  if (new_rts == 0)
+    dp->stop_ropc.ropc = 2;
+  else if (new_rts == 1)
+    dp->start_sopc.sopc = 2;
+}
+return 0;
+}
+
+static void mfc_stop_receive(struct m68k_async_struct *info)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0)
+  imask[(info->nr_uarts-1)/2] &= ~2;
+else
+  imask[(info->nr_uarts-1)/2] &= ~32;
+dp-> ir.imr = imask[(info->nr_uarts-1)/2];
+}
+
+static int mfc_trans_empty(struct m68k_async_struct *info)
+{
+struct duarthalf *dh;
+
+dh = (struct duarthalf *)info->port;
+return (dh->sr_csr.sr & 8);
+}
+
+int multiface_init(void)
+{
+unsigned int key;
+int line1, line2;
+struct duart *dp;
+int dummy;
+const struct ConfigDev *cd;
+struct serial_struct req;
+struct m68k_async_struct *info;
+
+if (!MACH_IS_AMIGA)
+  return -ENODEV;
+  
+nr_mfc = 0;
+/* add MFC_II and serial master for test purposes */
+while((key=zorro_find(ZORRO_PROD_BSC_MULTIFACE_III,1, 0))) {
+  board_index[nr_mfc/2] = key;
+  cd = zorro_get_board(key);
+  dp = (struct duart *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+DUARTBASE));
+
+  req.line = -1; /* first free ttyS? device */
+  req.type = SER_MFC_III;
+  req.port = (int)&dp->pa;
+  if ((line1 = m68k_register_serial( &req )) < 0) {
+      printk( "Cannot register MFC III serial port: no free device\n" );
+      return -EBUSY;
+  }
+  lines[nr_mfc++] = line1;
+  info = &rs_table[line1];
+  info->sw = &mfc_ser_switch;
+  info->nr_uarts = nr_mfc;
+  info->board_base = dp;
+
+  req.line = -1; /* first free ttyS? device */
+  req.type = SER_MFC_III;
+  req.port = (int)&dp->pb;
+  if ((line2 = m68k_register_serial( &req )) < 0) {
+      printk( "Cannot register MFC III serial port: no free device\n" );
+	  m68k_unregister_serial( line1 );
+      return -EBUSY;
+  }
+  lines[nr_mfc++] = line2;
+  info = &rs_table[line2];
+  info->sw = &mfc_ser_switch;
+  info->nr_uarts = nr_mfc--;
+  info->board_base = dp;
+
+  if (nr_mfc < 4) {
+      request_irq(IRQ_AMIGA_EXTER, mfc_interrupt, 0,
+                  "Multiface III serial", mfc_interrupt);
+      request_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter, 0,
+                  "Multiface III serial VBL", mfc_vbl_inter);
+  }
+
+  dp->ir.imr = 0; /* turn off all interrupts */
+  imask[nr_mfc/2] = 0;
+  dummy = dp->ir.isr; /* clear peding interrupts */
+  dummy = dp->ipcr_acr.ipcr;
+  dp->stop_ropc.ropc = 255; /* all outputs off */
+  dp->ipr_opcr.opcr = 0; /* change for serial-master */
+  dp->pa.cr = CR_RX_OFF; /* disable receiver */
+  dp->pb.cr = CR_RX_OFF;
+  dp->pa.cr = CR_TX_OFF; /* disable transmitter */
+  dp->pb.cr = CR_TX_OFF;
+  dp->pa.cr = CR_RESET_RX;
+  dp->pb.cr = CR_RESET_RX;
+  dp->pa.cr = CR_RESET_TX;
+  dp->pb.cr = CR_RESET_TX;
+  dp->pa.cr = CR_RESET_ERR;
+  dp->pb.cr = CR_RESET_ERR;
+  dp->pa.cr = CR_RESET_BREAK;
+  dp->pb.cr = CR_RESET_BREAK;
+  dp->pa.cr = CR_STOP_BREAK;
+  dp->pb.cr = CR_STOP_BREAK;
+  dp->pa.cr = CR_RESET_MR;
+  dp->pb.cr = CR_RESET_MR;
+  /* set default 9600  8 N 1 */
+  dp->pa.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON;
+  dp->pb.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON;
+  dp->pa.mr.mr2 = MR2_1STOP;
+  dp->pb.mr.mr2 = MR2_1STOP;
+
+  dp->ipcr_acr.acr = 0xe0; /* BRG set 2,  timer 1X crystal */
+  acmask[nr_mfc/2] = 0xe0;
+  /* change the following baudrate stuff for MFC_II */
+  dp->pa.sr_csr.csr = 0x99; /* 9600 from BRG */
+  dp->pb.sr_csr.csr = 0xdd; /* from timer */
+  dp->ctu = 0;
+  dp->ctl = 24; /* values for 9600 */
+  dummy = dp->start_sopc.start; /* load timer with new values */
+  nr_mfc++;
+
+  zorro_config_board(key,1);
+}
+return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return multiface_init();
+}
+
+void cleanup_module(void)
+{
+int i;
+struct duart *dp;
+
+for (i = 0; i < nr_mfc; i += 2) {
+  dp = rs_table[lines[i]].board_base;
+  dp->pa.cr = CR_RX_OFF | CR_TX_OFF; /* disable duart */
+  dp->pb.cr = CR_RX_OFF | CR_TX_OFF;
+  dp->ir.imr = 0;   /* turn off interrupts */
+  m68k_unregister_serial(lines[i]);
+  m68k_unregister_serial(lines[i+1]);
+  zorro_unconfig_board(board_index[i/2], 1);
+}
+free_irq(IRQ_AMIGA_EXTER, mfc_interrupt);
+free_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter);
+}
+#endif
+
+
diff -urN linux-2.2.18-pre15/drivers/char/ser_whippet.c linux/drivers/char/ser_whippet.c
--- linux-2.2.18-pre15/drivers/char/ser_whippet.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_whippet.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,837 @@
+#define WHIPPET_VER "2.2.0pre7"
+#define WHIPPET_REV 0
+#define WHIPPET_DATE "31/Jan/1999"
+
+/*
+ * ser_whippet.c - version info as above
+ *
+ * Copyright (C) 1997,98,99 Chris Sumner (chris@cpsumner.freeserve.co.uk)
+ *
+ * This is a driver for the Hisoft Whippet PCMCIA serial port for
+ * the Amiga. (16c550b UART)
+ *
+ * The code is mostly based on ser_ioext.c by Jes Sorensen,
+ * (jds@kom.auc.dk) but has been modified to cope with the different
+ * hardware footprint of the Whippet.
+ *
+ * Modified:
+ *
+ *   11/Feb/98 - General tidying up
+ *   31/Mar/98 - More tidying up
+ *    7/Apr/98 - Fixed nasty little bug concerning gayle ints
+ *    8/Apr/98 - Changed PCMCIA access timings to 100ns
+ *   31/Jul/98 - Changed email address
+ *    2/Nov/98 - Fixed to work with 2.1.124
+ *    7/Nov/98 - Added support for ASYNC_SPD_SHI and _WARP
+ *    8/Nov/98 - Fixed a few small bugs
+ *   20/Nov/98 - Re-structured code a bit and tidied up
+ *   21/Nov/98 - Added support for modules
+ *   22/Nov/98 - Fixed inverted DCD bug
+ *   23/Nov/98 - Re-structured interrupt code and fixed a few bugs - (2.1.127)
+ *   31/Jan/99 - Changed email (again) and modified for 2.2.0pre7
+ *
+ * To Do:
+ *
+ *   - Test at 230k4 and 460k8 rates
+ *   - Dynamic changing of fifo trigger level (via ioctl? via overrun count?)
+ *   - Handling of card insertion and removal (more than just shout at user!)
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/m68kserial.h>
+
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/amigayle.h>
+
+#ifdef gayle
+#undef gayle			/* We use our own definition */
+#endif
+
+#include "ser_whippet.h"
+
+#define WHIPPET_DEBUG	0	/* debug level */
+
+#define ser_DTRon(uart)  (uart->MCR |=  DTR)
+#define ser_RTSon(uart)  (uart->MCR |=  RTS)
+#define ser_DTRoff(uart) (uart->MCR &= ~DTR)
+#define ser_RTSoff(uart) (uart->MCR &= ~RTS)
+
+
+/***************************** Prototypes *****************************/
+static void ser_init(struct m68k_async_struct *info);
+static void ser_deinit(struct m68k_async_struct *info, int leave_dtr);
+static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag);
+static int  ser_check_custom_divisor(struct m68k_async_struct *info,
+				     int baud_base, int divisor);
+static void ser_change_speed(struct m68k_async_struct *info);
+static void ser_throttle(struct m68k_async_struct *info, int status);
+static void ser_set_break(struct m68k_async_struct *info, int break_flag);
+static void ser_get_serial_info(struct m68k_async_struct *info,
+				struct serial_struct *retinfo);
+static unsigned int ser_get_modem_info(struct m68k_async_struct *info);
+static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr,
+			      int new_rts);
+static int ser_ioctl(struct tty_struct *tty, struct file *file,
+		     struct m68k_async_struct *info, unsigned int cmd,
+		     unsigned long arg);
+static void ser_stop_receive(struct m68k_async_struct *info);
+static int ser_trans_empty(struct m68k_async_struct *info);
+/************************* End of Prototypes **************************/
+
+/*
+ * SERIALSWITCH structure for the Whippet serial interface.
+ */
+
+static SERIALSWITCH whippet_ser_switch = {
+	ser_init,
+	ser_deinit,
+	ser_enab_tx_int,
+	ser_check_custom_divisor,
+	ser_change_speed,
+	ser_throttle,
+	ser_set_break,
+	ser_get_serial_info,
+	ser_get_modem_info,
+	ser_set_modem_info,
+	ser_ioctl,
+	ser_stop_receive,
+	ser_trans_empty,
+	NULL
+};
+
+static int whippet_baud_table[20] = {
+	/* B0     */ 0,
+	/* B50    */ 9216,
+	/* B75    */ 6144,
+	/* B110   */ 4189,	/* 110.00238 */
+	/* B134.5 */ 3426,	/* 134.50087 */
+	/* B150   */ 3072,
+	/* B200   */ 2304,
+	/* B300   */ 1536,
+	/* B600   */ 768,
+	/* B1200  */ 384,
+	/* B1800  */ 256,
+	/* B2400  */ 192,
+	/* B4800  */ 96,
+	/* B9600  */ 48,
+	/* B19200 */ 24,
+	/* B38400 */ 12,	/* The last of the standard rates.  */
+	/* B57600 */ 8,		/* ASYNC_SPD_HI                     */
+	/* B115K2 */ 4,		/* ASYNC_SPD_VHI                    */
+	/* B230K4 */ 2,		/* ASYNC_SPD_SHI                    */
+	/* B460K8 */ 1		/* ASYNC_SPD_WARP                   */
+};
+
+
+static volatile struct GAYLE *gayle;		/* gayle register struct */
+static volatile struct WHIPPET *whippet;	/* whippet regs struct */
+
+static int fifo_trig_level=FIFO_TRIG_4;		/* can be changed */
+
+/*
+ * There are 4 receiver FIFO-interrupt trigger levels (FIFO_TRIG_x), that
+ * indicates how many bytes are to be allowed in the receiver-FIFO before
+ * an interrupt is generated:
+ *                x =  1 =  1 byte
+ *                x =  4 =  4 bytes
+ *                x =  8 =  8 bytes
+ *                x = 14 = 14 bytes
+ * If you keep getting overruns try lowering this value one step at a time.
+ */
+
+/***** start of ser_interrupt() - Handler for serial interrupt. *****/
+
+static void ser_interrupt(int irq, void *data, struct pt_regs *regs)
+{
+struct m68k_async_struct *info = data;
+u_char iir,ier,lsr,gayleirq = gayle->intreq;
+
+	if ((gayleirq & 0x7c)==0) return;	/* quick exit */
+
+	if (gayleirq & 0x5c) {
+		gayle->intreq = ((gayleirq & 0x5c) ^ 0x5c) | (GAYLE_IRQ_IDE | GAYLE_IRQ_SC);
+		printk("gayle->intreq = 0x%02x; gayle->inten = 0x%02x\n",gayleirq, gayle->inten);
+		if (gayleirq & GAYLE_IRQ_CCDET) {
+			if (gayle->cardstatus & GAYLE_CS_CCDET) {
+				printk("Card inserted! Don't do that!\n");
+			} else {
+				printk("Card removed! Don't do that!\n");
+			}
+		}
+	}
+
+	if ((gayleirq & GAYLE_IRQ_SC)==0) return;
+
+/* If we got here, then there is an interrupt waiting for us to service */
+
+	iir = whippet->IIR;
+
+/* Disable UART interrupts for now... */
+
+	ier = whippet->IER; whippet->IER = 0;
+
+	while (!(iir & IRQ_PEND)) {	/* loop until no more ints */
+
+		switch (iir & (IRQ_ID1 | IRQ_ID2 | IRQ_ID3)) {
+			case IRQ_RLS:		/* Receiver Line Status */
+			case IRQ_CTI:		/* Character Timeout */
+			case IRQ_RDA:		/* Received Data Available */
+	/*
+	 * Copy chars to the tty-queue ...
+	 * Be careful that we aren't passing one of the
+	 * Receiver Line Status interrupt-conditions without noticing.
+	 */
+			{
+				int ch;
+
+				lsr = whippet->LSR;
+				while (lsr & DR) {
+					u_char err;
+					ch = whippet->RBR;
+
+					if (lsr & BI)      err = TTY_BREAK;
+					else if (lsr & PE) err = TTY_PARITY;
+					else if (lsr & OE) err = TTY_OVERRUN;
+					else if (lsr & FE) err = TTY_FRAME;
+					else err = 0;
+
+					rs_receive_char(info, ch, err);
+					lsr = whippet->LSR;
+				}
+			}
+			break;
+
+			case IRQ_THRE:	/* Transmitter holding register empty */
+			{
+				int fifo_space = FIFO_SIZE;
+
+	/* If the uart is ready to receive data and there are chars in */
+	/* the queue we transfer all we can to the uart's FIFO         */
+
+				if (rs_no_more_tx(info)) {
+#if WHIPPET_DEBUG
+					printk("rs_no_more_tx()\n");
+#endif
+		/* Disable transmitter empty interrupt */
+					ier &= ~(ETHREI);
+
+		/* Read IIR to acknowledge the interrupt */
+					(void)whippet->IIR;
+					break;
+				}
+
+		/* Handle software flow control */
+				if (info->x_char) {
+#if WHIPPET_DEBUG
+					printk("Flow: X%s\n",(info->x_char == 19) ? "OFF" : "ON");
+#endif
+					whippet->THR = info->x_char;
+					info->icount.tx++;
+					info->x_char = 0;
+					fifo_space--;
+				}
+
+		/* Fill the fifo */
+				while (fifo_space > 0) {
+					fifo_space--;
+					whippet->THR = info->xmit_buf[info->xmit_tail++];
+					info->xmit_tail &= (SERIAL_XMIT_SIZE-1);
+					info->icount.tx++;
+					if (--info->xmit_cnt == 0) break;
+				}
+#if WHIPPET_DEBUG
+				if (fifo_space == 0) printk("fifo full\n");
+#endif
+		/* Don't need THR interrupts any more */
+				if (info->xmit_cnt == 0) {
+#if WHIPPET_DEBUG
+					printk("TX ints OFF\n");
+#endif
+					ier &= ~(ETHREI);
+				}
+
+				if (info->xmit_cnt < WAKEUP_CHARS) {
+#if WHIPPET_DEBUG
+					printk("rs_sched_event()\n");
+#endif
+					rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				}
+			}
+			break;
+
+			case IRQ_MS: /* Must be modem status register interrupt? */
+			{
+				u_char msr = whippet->MSR;
+
+				if (info->flags & ASYNC_INITIALIZED) {
+					if (msr & DCTS) {
+#if WHIPPET_DEBUG
+						printk("CTS = %s\n",(msr & CTS) ? "ON" : "OFF");
+#endif
+						rs_check_cts(info, (msr & CTS));
+					}
+					if (msr & DDCD)
+						rs_dcd_changed(info, (msr & DCD));
+				}
+			}
+			break;
+
+		} /* switch (iir) */
+
+		iir = whippet->IIR;
+	} /* while IRQ_PEND */
+
+/* Acknowledge gayle STATUS_CHANGE interrupt */
+
+	gayle->intreq = ((gayleirq & GAYLE_IRQ_SC) ^ GAYLE_IRQ_SC) | GAYLE_IRQ_IDE;
+
+/* Re-enable UART interrupts */
+
+	whippet->IER = ier;
+}
+
+/***** end of ser_interrupt() *****/
+
+
+/***** start of ser_init() *****/
+
+static void ser_init(struct m68k_async_struct *info)
+{
+#if WHIPPET_DEBUG
+	printk("ser_init()\n");
+#endif
+	while ((whippet->LSR) & DR)
+		(void)whippet->RBR;		/* read a byte */
+
+/* Set DTR and RTS */
+	whippet->MCR |= (DTR | RTS);
+
+/* Enable interrupts. IF_PORTS irq has already been enabled in whippet_init()*/
+/* DON'T enable ETHREI here because there is nothing to send yet (murray)    */
+	whippet->IER |= (ERDAI | ELSI | EMSI);
+
+	MOD_INC_USE_COUNT;
+}
+
+/***** end of ser_init() *****/
+
+
+/***** start of ser_deinit() *****/
+
+static void ser_deinit(struct m68k_async_struct *info, int leave_dtr)
+{
+#if WHIPPET_DEBUG
+	printk("ser_deinit()\n");
+#endif
+	/* Wait for the uart to get empty */
+	while (!((whippet->LSR) & TEMT)) {
+	}
+
+	while ((whippet->LSR) & DR) {
+		(void)whippet->RBR;
+	}
+
+/* No need to disable UART interrupts since this will already
+ * have been done via ser_enab_tx_int() and ser_stop_receive()
+ */
+
+	ser_RTSoff(whippet);
+	if (!leave_dtr)	ser_DTRoff(whippet);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/***** end of ser_deinit() *****/
+
+
+/***** start of ser_enab_tx_int() *****/
+
+/*
+** Enable or disable tx interrupts.
+** Note that contrary to popular belief, it is not necessary to
+** send a character to cause an interrupt to occur. Whenever the
+** THR is empty and THRE interrupts are enabled, an interrupt will occur.
+** (murray)
+*/
+static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag)
+{
+#if WHIPPET_DEBUG
+	printk("ser_enab_tx_int(%s)\n",(enab_flag) ? "ON" : "OFF");
+#endif
+	if (enab_flag)	whippet->IER |= ETHREI;
+	else		whippet->IER &= ~(ETHREI);
+}
+
+/***** end of ser_enab_tx_int() *****/
+
+
+static int  ser_check_custom_divisor(struct m68k_async_struct *info,
+				     int baud_base, int divisor)
+{
+#if WHIPPET_DEBUG
+	printk("ser_check_custom_divisor()\n");
+#endif
+	/* Always return 0 or else setserial spd_hi/spd_vhi doesn't work */
+	return 0;
+}
+
+
+/***** start of ser_change_speed() *****/
+
+static void ser_change_speed(struct m68k_async_struct *info)
+{
+u_int cflag, baud, chsize, stopb, parity, aflags;
+u_int div = 0, ctrl = 0;
+
+#if WHIPPET_DEBUG
+	printk("ser_change_speed()\n");
+#endif
+
+	if (!info->tty || !info->tty->termios) return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)	info->flags |= ASYNC_CTS_FLOW;
+	else			info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)	info->flags &= ~ASYNC_CHECK_CD;
+	else			info->flags |= ASYNC_CHECK_CD;
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		if (aflags == ASYNC_SPD_HI)	/*  57k6 */
+			baud += 1;
+		if (aflags == ASYNC_SPD_VHI)	/* 115k2 */
+			baud += 2;
+		if (aflags == ASYNC_SPD_SHI)	/* 230k4 */
+			baud += 3;
+		if (aflags == ASYNC_SPD_WARP)	/* 460k8 */
+			baud += 4;
+		if (aflags == ASYNC_SPD_CUST)
+			div = info->custom_divisor;
+	}
+	if (!div) {
+		/* Maximum speed is 460800 */
+			if (baud > 19) baud = 19;
+			div = whippet_baud_table[baud];
+	}
+
+#if WHIPPET_DEBUG
+	printk("divisor=%d, baud rate=%d\n",div,(div==0)? -1 : WHIPPET_BAUD_BASE/div);
+#endif
+
+	if (!div) {
+		/* speed == 0 -> drop DTR */
+		ser_DTRoff(whippet);
+		return;
+	}
+
+/*
+ * We have to set DTR when a valid rate is chosen, otherwise DTR
+ * might get lost when programs use this sequence to clear the line:
+ *
+ * change_speed(baud = B0);
+ * sleep(1);
+ * change_speed(baud = Bx); x != 0
+ *
+ * The pc-guys do this aswell.
+ */
+	ser_DTRon(whippet);
+
+	if (chsize == CS8)	ctrl |= data_8bit;
+	else if (chsize == CS7)	ctrl |= data_7bit;
+	else if	(chsize == CS6)	ctrl |= data_6bit;
+	else if (chsize == CS5)	ctrl |= data_5bit;
+
+/* If stopb is true we set STB which means 2 stop-bits */
+/* otherwise we only get 1 stop-bit.                   */
+
+	ctrl |= (stopb ? STB : 0);
+	ctrl |= ((parity & PARENB) ? ((parity & PARODD) ? (PEN) : (PEN |
+							  EPS)) : 0x00 );
+
+	whippet->LCR = (ctrl | DLAB);
+
+		/* Store high byte of divisor */
+
+	whippet->DLM = ((div >> 8) & 0xff);
+
+		/* Store low byte of divisor */
+
+	whippet->DLL = (div & 0xff);
+	whippet->LCR = ctrl;
+}
+
+/***** end of ser_change_speed() *****/
+
+
+/***** start of ser_throttle() *****/
+
+static void ser_throttle(struct m68k_async_struct *info, int status)
+{
+#if WHIPPET_DEBUG
+	printk("ser_throttle(rts=%s)\n", (status) ? "OFF" : "ON");
+#endif
+	if (status)	ser_RTSoff(whippet);
+	else		ser_RTSon(whippet);
+}
+
+/***** end of ser_throttle() *****/
+
+
+/***** start of ser_set_break() *****/
+
+static void ser_set_break(struct m68k_async_struct *info, int break_flag)
+{
+#if WHIPPET_DEBUG
+	printk("ser_set_break(%s)\n", (break_flag) ? "ON" : "OFF");
+#endif
+	if (break_flag)	whippet->LCR |= SET_BREAK;
+	else		whippet->LCR &= ~SET_BREAK;
+}
+
+/***** end of ser_set_break() *****/
+
+
+/***** start of ser_get_serial_info() *****/
+
+static void ser_get_serial_info(struct m68k_async_struct *info,
+				struct serial_struct *retinfo)
+{
+#if WHIPPET_DEBUG
+	printk("ser_get_serial_info()\n");
+#endif
+
+	retinfo->baud_base = WHIPPET_BAUD_BASE;
+	retinfo->xmit_fifo_size = FIFO_SIZE;	/* This field is currently ignored, */
+						/* by the upper layers of the       */
+						/* serial-driver.                   */
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+/***** end of ser_get_serial_info() *****/
+
+
+/***** start of ser_get_modem() *****/
+
+static unsigned int ser_get_modem_info(struct m68k_async_struct *info)
+{
+unsigned char msr, mcr;
+
+#if WHIPPET_DEBUG > 1
+	printk("ser_get_modem()\n");
+#endif
+
+	msr = whippet->MSR;
+	mcr = whippet->MCR;	/* The DTR and RTS are located in the */
+				/* ModemControlRegister ...           */
+
+	return (
+		((mcr & DTR) ? TIOCM_DTR : 0) |
+		((mcr & RTS) ? TIOCM_RTS : 0) |
+
+		((msr & DCD) ? TIOCM_CAR : 0) |
+		((msr & CTS) ? TIOCM_CTS : 0) |
+		((msr & DSR) ? TIOCM_DSR : 0) |
+		((msr & RING_I) ? TIOCM_RNG : 0)
+	);
+}
+
+/***** end of ser_get_modem() *****/
+
+
+/***** start of ser_set_modem_info() *****/
+
+static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr,
+			      int new_rts)
+{
+#if WHIPPET_DEBUG
+	printk("ser_set_modem(dtr=%s, rts=%s)\n",(new_dtr == 0) ? "OFF" : "ON",(new_rts == 0) ? "OFF" : "ON");
+#endif
+
+	if (new_dtr == 0)	ser_DTRoff(whippet);
+	else if (new_dtr == 1)	ser_DTRon(whippet);
+
+	if (new_rts == 0)	ser_RTSoff(whippet);
+	else if (new_rts == 1)	ser_RTSon(whippet);
+
+	return 0;
+};
+
+/***** end of ser_set_modem_info() *****/
+
+
+/***** start of ser_stop_receive() *****/
+
+static void ser_stop_receive (struct m68k_async_struct *info)
+{
+#if WHIPPET_DEBUG
+	printk("ser_stop_receive()\n");
+#endif
+	/* Disable uart receive and status interrupts */
+	whippet->IER &= ~(ERDAI | ELSI | EMSI);
+}
+
+/***** end of ser_stop_receive() *****/
+
+
+/***** start of ser_trans_empty() *****/
+
+static int ser_trans_empty (struct m68k_async_struct *info)
+{
+#if WHIPPET_DEBUG
+	printk("ser_trans_empty()\n");
+#endif
+	return (whippet->LSR & THRE);
+}
+
+/***** end of ser_trans_empty() *****/
+
+
+/***** start of ser_ioctl() *****/
+static int ser_ioctl(struct tty_struct *tty, struct file *file,
+		     struct m68k_async_struct *info, unsigned int cmd,
+		     unsigned long arg)
+{
+/*	switch(cmd) {
+		case:
+	}*/
+	return -ENOIOCTLCMD;
+}
+
+/***** end of ser_ioctl() *****/
+
+
+/***** start of ser_reset_port() *****/
+
+void ser_reset_port(void)
+{
+#if WHIPPET_DEBUG
+	printk("ser_reset_port()\n");
+#endif
+/*
+ * Try and reset the serial port to a default state
+ */
+	whippet->IER = 0x00;	/* disable interrupts */
+	(void)whippet->IIR;	/* clear any pending misc interrupts */
+	(void)whippet->LSR;	/* clear any pending LSR interrupts */
+	(void)whippet->MSR;	/* clear any pending MSR interrupts */
+
+/*
+ * Set the serial port to a default setting of 8N1 - 9600
+ */
+	whippet->LCR = (data_8bit | DLAB);
+	whippet->DLM = 0;
+	whippet->DLL = 48;
+	whippet->LCR = (data_8bit);
+
+/*
+ * Set the rx FIFO-trigger count.
+ */
+	whippet->FCR = (RCVR_FIFO_RES | FIFO_ENA |
+			XMIT_FIFO_RES | fifo_trig_level );
+	whippet->MCR = 0;
+}
+
+/***** end of ser_reset_port() *****/
+
+
+/***** start of whippet_init() *****/
+
+/*
+ * Detect and initialize any Whippet found in the system.
+ */
+
+static int line;	/* The line assigned to us by register_serial() */
+
+static struct m68k_async_struct *amiga_info;	/* our async struct */
+
+int whippet_init(void)
+{
+unsigned long flags;
+struct serial_struct req;
+struct m68k_async_struct *info;
+
+#if WHIPPET_DEBUG
+	printk("whippet_init()\n");
+#endif
+	if (!(MACH_IS_AMIGA))
+		return -ENODEV;
+
+	if ((amiga_model!=AMI_1200) && (amiga_model!=AMI_600))
+		return -ENODEV;
+
+	if (!(AMIGAHW_PRESENT(PCMCIA)))	/* might be missing, you never know! */
+		return -ENODEV;
+
+/*
+ * Initialise hardware structure pointers
+ */
+	gayle = (struct GAYLE *)(zTwoBase + GAYLE_ADDRESS);
+	whippet = (struct WHIPPET *)(zTwoBase + WHIPPET_PHYSADDR);
+
+#if WHIPPET_DEBUG
+	printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus);
+	printk("gayle->intreq = 0x%02x\n",gayle->intreq);
+	printk("gayle->inten  = 0x%02x\n",gayle->inten);
+	printk("gayle->config = 0x%02x\n",gayle->config);
+#endif
+	printk("Probing for Whippet serial port... (v%s r%i - %s)\n",WHIPPET_VER, WHIPPET_REV, WHIPPET_DATE);
+
+/*
+ * Test gayle cardstatus bits for presence of a card
+ */
+	if (!(gayle->cardstatus & GAYLE_CS_CCDET)) {
+		printk("No PCMCIA Card detected\n");
+		return -ENODEV;
+#if WHIPPET_DEBUG
+	} else { printk("PCMCIA Card detected\n");
+#endif
+	}
+
+/*
+ * Card detected, but is it a Whippet??? Let's try and find out...
+ */
+	{
+	u_char ch1,ch2;
+		whippet->SCR = 0x42;
+		whippet->IER = 0x00;
+		ch1=whippet->SCR;	/* should be 0x42 */
+		whippet->SCR = 0x99;
+		whippet->IER = 0x00;
+		ch2=whippet->SCR;	/* should be 0x99 */
+		if ((ch1!=0x42) || (ch2!=0x99)) return -ENODEV;
+	}
+
+	ser_reset_port();	/* initialise the serial port */
+
+/*
+ * Set the necessary tty-stuff.
+ */
+	req.line = -1;			/* first free ttyS? device */
+	req.type = SER_WHIPPET;
+	req.port = (int) &(whippet->RBR);
+
+	if ((line = register_serial( &req )) < 0) {
+		printk( "Cannot register Whippet serial port: no free device\n" );
+		return -EBUSY;
+	}
+
+	info = &rs_table[line];	/* set info == struct *m68k_async_struct */
+
+	info->nr_uarts = 1;			/* one UART         */
+	info->sw = &whippet_ser_switch;		/* switch functions */
+	info->icount.cts = info->icount.dsr = 0;
+	info->icount.rng = info->icount.dcd = 0;
+	info->icount.rx = info->icount.tx = 0;
+	info->icount.frame = info->icount.parity = 0;
+	info->icount.overrun = info->icount.brk = 0;
+
+	amiga_info = info;	/* initialise our static async struct */
+
+/*
+ * Clear any spurious interrupts in gayle
+ */
+	gayle->intreq = ((gayle->intreq & 0x6c) ^ 0x6c) | GAYLE_IRQ_IDE;
+
+/*
+ * Install ISR - level 2 - data is struct *m68k_async_struct
+ */
+	request_irq(IRQ_AMIGA_PORTS, ser_interrupt, 0, "whippet serial", info);
+
+	save_flags(flags);
+	cli();
+
+#if WHIPPET_DEBUG
+	printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus);
+	printk("gayle->intreq = 0x%02x\n",gayle->intreq);
+	printk("gayle->inten  = 0x%02x\n",gayle->inten);
+	printk("gayle->config = 0x%02x\n",gayle->config);
+#endif
+
+/*
+ * Enable status_change interrupts in gayle
+ */
+
+	gayle->inten |= GAYLE_IRQ_SC;
+	gayle->cardstatus = GAYLE_CS_WR | GAYLE_CS_DA;
+	gayle->config = 0;
+
+#if WHIPPET_DEBUG
+	printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus);
+	printk("gayle->intreq = 0x%02x\n",gayle->intreq);
+	printk("gayle->inten  = 0x%02x\n",gayle->inten);
+	printk("gayle->config = 0x%02x\n",gayle->config);
+#endif
+
+	restore_flags(flags);
+
+#if WHIPPET_DEBUG
+	printk("Detected Whippet Serial Port at 0x%08x (ttyS%i)\n",(int)whippet,line);
+#endif
+	return 0;
+}
+
+/***** end of whippet_init() *****/
+
+
+/***** Module functions *****/
+
+#ifdef MODULE
+int init_module(void)
+{
+	return whippet_init();
+}
+
+void cleanup_module(void)
+{
+#if WHIPPET_DEBUG
+	printk("Closing Whippet Device!\n");
+#endif
+	unregister_serial(line);
+
+#if WHIPPET_DEBUG
+	printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus);
+	printk("gayle->intreq = 0x%02x\n",gayle->intreq);
+	printk("gayle->inten  = 0x%02x\n",gayle->inten);
+	printk("gayle->config = 0x%02x\n",gayle->config);
+#endif
+	ser_reset_port();
+
+	gayle->cardstatus = 0;
+	gayle->intreq = ((gayle->intreq & 0x6c) ^ 0x6c) | GAYLE_IRQ_IDE;
+	gayle->inten &= GAYLE_IRQ_IDE;
+
+#if WHIPPET_DEBUG
+	printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus);
+	printk("gayle->intreq = 0x%02x\n",gayle->intreq);
+	printk("gayle->inten  = 0x%02x\n",gayle->inten);
+	printk("gayle->config = 0x%02x\n",gayle->config);
+#endif
+	free_irq(IRQ_AMIGA_PORTS,amiga_info);
+}
+#endif
+
+/***** end of Module functions *****/
diff -urN linux-2.2.18-pre15/drivers/char/ser_whippet.h linux/drivers/char/ser_whippet.h
--- linux-2.2.18-pre15/drivers/char/ser_whippet.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/ser_whippet.h	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,130 @@
+/*
+ * Defines for the Hisoft Whippet serial port for the Amiga 600/1200
+ * range of computers.
+ *
+ * This code is (C) 1997,98 Chris Sumner (chris@cpsumner.freeserve.co.uk),
+ * based on 16c552.h and ser_ioext.h which are (C) 1995 Jes Sorensen.
+ * (jds@kom.auc.dk)
+ */
+
+#ifndef _SER_WHIPPET_H_
+#define _SER_WHIPPET_H_
+
+#define UART_CLK             7372800
+#define WHIPPET_BAUD_BASE   (UART_CLK / 16)
+
+#define WHIPPET_PHYSADDR    (0xA30600)	/* from whippet.device */
+
+struct WHIPPET {
+	u_char  RBR;		/* Reciever Buffer Register */
+	u_char  pad0[0xfff];
+	u_char  IER;		/* Interrupt Enable Register */
+	u_char  pad1[0xfff];
+	u_char  IIR;		/* Interrupt Identification Register */
+	u_char  pad2[0xfff];
+	u_char  LCR;		/* Line Control Register */
+	u_char  pad3[0xfff];
+	u_char  MCR;		/* Modem Control Register */
+	u_char  pad4[0xfff];
+	u_char  LSR;		/* Line Status Register */
+	u_char  pad5[0xfff];
+	u_char  MSR;		/* Modem Status Register */
+	u_char  pad6[0xfff];
+	u_char  SCR;		/* Scratch Register */
+};
+
+#define THR RBR   /* Transmitter Holding Register */
+#define FCR IIR   /* FIFO Control Register */
+#define DLL RBR   /* Divisor Latch - LSB */
+#define DLM IER   /* Divisor Latch - MSB */
+
+/*
+ * Bit-defines for the various registers.
+ */
+
+/* IER - Interrupt Enable Register */
+
+#define ERDAI         (1<<0)
+#define ETHREI        (1<<1)
+#define ELSI          (1<<2)
+#define EMSI          (1<<3)
+
+/* IIR - Interrupt Ident. Register */
+
+#define IRQ_PEND      (1<<0) /* NOTE: IRQ_PEND=0 implies irq pending */
+#define IRQ_ID1       (1<<1)
+#define IRQ_ID2       (1<<2)
+#define IRQ_ID3       (1<<3)
+#define FIFO_ENA0     (1<<6) /* Both these are set when FCR(1<<0)=1 */
+#define FIFO_ENA1     (1<<7)
+
+#define IRQ_RLS   (IRQ_ID1 | IRQ_ID2)
+#define IRQ_RDA    IRQ_ID2
+#define IRQ_CTI   (IRQ_ID2 | IRQ_ID3)
+#define IRQ_THRE   IRQ_ID1
+#define IRQ_MS     0
+
+/* FCR - FIFO Control Register */
+
+#define FIFO_ENA      (1<<0)
+#define RCVR_FIFO_RES (1<<1)
+#define XMIT_FIFO_RES (1<<2)
+#define DMA_MODE_SEL  (1<<3)
+#define RCVR_TRIG_LSB (1<<6)
+#define RCVR_TRIG_MSB (1<<7)
+
+#define FIFO_TRIG_1   0x00
+#define FIFO_TRIG_4   RCVR_TRIG_LSB
+#define FIFO_TRIG_8   RCVR_TRIG_MSB
+#define FIFO_TRIG_14 (RCVR_TRIG_LSB | RCVR_TRIG_MSB)
+
+#define FIFO_SIZE     16
+
+/* LCR - Line Control Register */
+
+#define WLS0          (1<<0)
+#define WLS1          (1<<1)
+#define STB           (1<<2)
+#define PEN           (1<<3)
+#define EPS           (1<<4)
+#define STICK_PARITY  (1<<5)
+#define SET_BREAK     (1<<6)
+#define DLAB          (1<<7)
+
+#define data_5bit      0x00
+#define data_6bit      0x01
+#define data_7bit      0x02
+#define data_8bit      0x03
+
+
+/* MCR - Modem Control Register */
+
+#define DTR           (1<<0)
+#define RTS           (1<<1)
+#define OUT1          (1<<2)
+#define OUT2          (1<<3)
+#define LOOP          (1<<4)
+
+/* LSR - Line Status Register */
+
+#define DR            (1<<0)
+#define OE            (1<<1)
+#define PE            (1<<2)
+#define FE            (1<<3)
+#define BI            (1<<4)
+#define THRE          (1<<5)
+#define TEMT          (1<<6)
+#define RCVR_FIFO_ERR (1<<7)
+
+/* MSR - Modem Status Register */
+
+#define DCTS          (1<<0)
+#define DDSR          (1<<1)
+#define TERI          (1<<2)
+#define DDCD          (1<<3)
+#define CTS           (1<<4)
+#define DSR           (1<<5)
+#define RING_I        (1<<6)
+#define DCD           (1<<7)
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/char/serial167.c linux/drivers/char/serial167.c
--- linux-2.2.18-pre15/drivers/char/serial167.c	Wed May  3 17:16:38 2000
+++ linux/drivers/char/serial167.c	Mon Dec  3 20:34:10 2001
@@ -42,7 +42,7 @@
 #include <linux/timer.h>
 #include <linux/tty.h>
 #include <linux/interrupt.h>
-#include <linux/serial.h>
+#include <linux/m68kserial.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
@@ -2788,6 +2788,57 @@
 	base_addr[CyIER] = ier;
 
 	restore_flags(flags);
+}
+
+/* This is a hack; if there are multiple chars waiting in the chip we
+ * discard all but the last one, and return that.  The cd2401 is not really
+ * designed to be driven in polled mode.
+ */
+
+int serial167_wait_key(struct console *co)
+{
+	volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+	unsigned long flags;
+	volatile u_char sink;
+	u_char ier;
+	int port;
+	int keypress = 0;
+
+	save_flags(flags); cli();
+
+	/* Ensure receiver is enabled! */
+
+	port = 0;
+	base_addr[CyCAR] = (u_char)port;
+	while (base_addr[CyCCR])
+		;
+	base_addr[CyCCR] = CyENB_RCVR;
+	ier = base_addr[CyIER];
+	base_addr[CyIER] = CyRxData;
+
+	while (!keypress) {
+		if (pcc2chip[PccSCCRICR] & 0x20)
+		{
+			/* We have an Rx int. Acknowledge it */
+			sink = pcc2chip[PccRPIACKR];
+			if ((base_addr[CyLICR] >> 2) == port) {
+				int cnt = base_addr[CyRFOC];
+				while (cnt-- > 0)
+				{
+					keypress = base_addr[CyRDR];
+				}
+				base_addr[CyREOIR] = 0;
+			}
+			else
+				base_addr[CyREOIR] = CyNOTRANS;
+		}
+	}
+
+	base_addr[CyIER] = ier;
+
+	restore_flags(flags);
+
+	return keypress;
 }
 
 #ifdef CONFIG_REMOTE_DEBUG
diff -urN linux-2.2.18-pre15/drivers/char/sx.c linux/drivers/char/sx.c
--- linux-2.2.18-pre15/drivers/char/sx.c	Mon Sep  4 10:39:18 2000
+++ linux/drivers/char/sx.c	Mon Dec  3 20:34:10 2001
@@ -2523,6 +2523,7 @@
 			else
 				pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2,
 						      &tint);
+			board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK;
 			board->base2 = 
 			board->base = (ulong) ioremap(board->hw_base, WINDOW_LEN (board));
 			if (!board->base) {
diff -urN linux-2.2.18-pre15/drivers/isdn/act2000/module.c linux/drivers/isdn/act2000/module.c
--- linux-2.2.18-pre15/drivers/isdn/act2000/module.c	Tue Jan  4 10:12:15 2000
+++ linux/drivers/isdn/act2000/module.c	Mon Dec  3 20:34:10 2001
@@ -279,9 +279,7 @@
 	act2000_receive(card);
         save_flags(flags);
         cli();
-        del_timer(&card->ptimer);
-        card->ptimer.expires = jiffies + 3;
-        add_timer(&card->ptimer);
+        mod_timer(&card->ptimer, jiffies+3);
         restore_flags(flags);
 }
 
diff -urN linux-2.2.18-pre15/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- linux-2.2.18-pre15/drivers/isdn/icn/icn.c	Tue Oct 26 17:53:40 1999
+++ linux/drivers/isdn/icn/icn.c	Mon Dec  3 20:34:10 2001
@@ -602,9 +602,7 @@
 		/* schedule b-channel polling again */
 		save_flags(flags);
 		cli();
-		del_timer(&card->rb_timer);
-		card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
-		add_timer(&card->rb_timer);
+		mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD);
 		card->flags |= ICN_FLAGS_RBTIMER;
 		restore_flags(flags);
 	} else
@@ -905,9 +903,7 @@
 	/* schedule again */
 	save_flags(flags);
 	cli();
-	del_timer(&card->st_timer);
-	card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-	add_timer(&card->st_timer);
+	mod_timer(&card->st_timer, jiffies+ICN_TIMER_DCREAD);
 	restore_flags(flags);
 }
 
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- linux-2.2.18-pre15/drivers/isdn/isdn_audio.c	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_audio.c	Mon Dec  3 20:34:10 2001
@@ -183,9 +183,9 @@
 };
 
 #define NCOEFF           16     /* number of frequencies to be analyzed       */
-#define DTMF_TRESH    50000     /* above this is dtmf                         */
-#define SILENCE_TRESH   100     /* below this is silence                      */
-#define H2_TRESH      10000     /* 2nd harmonic                               */
+#define DTMF_TRESH    25000     /* above this is dtmf                         */
+#define SILENCE_TRESH   200     /* below this is silence                      */
+#define H2_TRESH      20000     /* 2nd harmonic                               */
 #define AMP_BITS          9     /* bits per sample, reduced to avoid overflow */
 #define LOGRP             0
 #define HIGRP             1
@@ -225,38 +225,25 @@
 	{'*', '0', '#', 'D'}
 };
 
-
-/*
- * egcs 2.95 complain about invalid asm statement:
- * "fixed or forbidden register 2 (cx) was spilled for class CREG."
- */
-#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) && defined(__GNUC__)
-#if __GNUC__ == 2 && __GNUC_MINOR__ < 95
-#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
-#endif
-#endif
-
-#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
 static inline void
-isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
+isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n)
 {
-	__asm__("cld\n"
+#ifdef __i386__
+	unsigned long d0, d1, d2, d3;
+	__asm__ __volatile__(
+		"cld\n"
 		"1:\tlodsb\n\t"
 		"xlatb\n\t"
 		"stosb\n\t"
 		"loop 1b\n\t"
-      : :  "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff)
-      :        "bx", "cx", "di", "si", "ax");
-}
-
+	:	"=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
+	:	"0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
+	:	"memory", "ax");
 #else
-static inline void
-isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
-{
 	while (n--)
 		*buff++ = table[*(unsigned char *)buff];
-}
 #endif
+}
 
 void
 isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- linux-2.2.18-pre15/drivers/isdn/isdn_common.c	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_common.c	Mon Dec  3 20:34:10 2001
@@ -69,6 +69,7 @@
 
 static int isdn_writebuf_stub(int, int, const u_char *, int, int);
 static void set_global_features(void);
+static int isdn_wildmat(char *s, char *p);
 
 void
 isdn_MOD_INC_USE_COUNT(void)
@@ -150,7 +151,7 @@
  * [^xyz]  matches any single character not in the set of characters
  */
 
-int
+static int
 isdn_wildmat(char *s, char *p)
 {
 	register int last;
@@ -196,6 +197,23 @@
 	return (*s == '\0')?0:nostar;
 }
 
+int isdn_msncmp( const char * msn1, const char * msn2 )
+{
+	char TmpMsn1[ ISDN_MSNLEN ];
+	char TmpMsn2[ ISDN_MSNLEN ];
+	char *p;
+
+	for ( p = TmpMsn1; *msn1 && *msn1 != ':'; )  // Strip off a SPID
+		*p++ = *msn1++;
+	*p = '\0';
+
+	for ( p = TmpMsn2; *msn2 && *msn2 != ':'; )  // Strip off a SPID
+		*p++ = *msn2++;
+	*p = '\0';
+
+	return isdn_wildmat( TmpMsn1, TmpMsn2 );
+}
+
 static void
 isdn_free_queue(struct sk_buff_head *queue)
 {
@@ -267,9 +285,7 @@
 
 		save_flags(flags);
 		cli();
-		del_timer(&dev->timer);
-		dev->timer.expires = jiffies + ISDN_TIMER_RES;
-		add_timer(&dev->timer);
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
 		restore_flags(flags);
 	}
 }
@@ -290,11 +306,8 @@
 		dev->tflags |= tf;
 	else
 		dev->tflags &= ~tf;
-	if (dev->tflags) {
-		if (!del_timer(&dev->timer))	/* del_timer is 1, when active */
-			dev->timer.expires = jiffies + ISDN_TIMER_RES;
-		add_timer(&dev->timer);
-	}
+	if (dev->tflags)
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
 	restore_flags(flags);
 }
 
@@ -472,7 +485,7 @@
 				return 0;
 			}
 			/* Try to find a network-interface which will accept incoming call */
-			r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, c->parm.setup));
+			r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup));
 			switch (r) {
 				case 0:
 					/* No network-device replies.
@@ -481,7 +494,7 @@
 					 * 3 on eventually match, if CID is longer.
 					 */
                                         if (c->command == ISDN_STAT_ICALL)
-					  if ((retval = isdn_tty_find_icall(di, c->arg, c->parm.setup))) return(retval);
+					  if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);
 #ifdef CONFIG_ISDN_DIVERSION 
                                          if (divert_if)
                  	                  if ((retval = divert_if->stat_callback(c))) 
@@ -1027,7 +1040,7 @@
 }
 
 static loff_t
-isdn_lseek(struct file *file, loff_t offset, int orig)
+isdn_llseek(struct file *file, loff_t offset, int orig)
 {
 	return -ESPIPE;
 }
@@ -1661,7 +1674,7 @@
 
 static struct file_operations isdn_fops =
 {
-	llseek:		isdn_lseek,
+	llseek:		isdn_llseek,
 	read:		isdn_read,
 	write:		isdn_write,
 	poll:		isdn_poll,
@@ -1846,7 +1859,6 @@
 		skb_pull(nskb, sizeof(int));
 		if (!nskb->len) {
 			dev_kfree_skb(nskb);
-			dev_kfree_skb(skb);
 			return v110_ret;
 		}
 		/* V.110 must always be acknowledged */
@@ -1885,9 +1897,10 @@
 			atomic_inc(&dev->v110use[idx]);
 			dev->v110[idx]->skbuser++;
 			atomic_dec(&dev->v110use[idx]);
-			dev_kfree_skb(skb);
 			/* For V.110 return unencoded data length */
 			ret = v110_ret;
+			/* if the complete frame was send we free the skb;
+			   if not upper function will requeue the skb */ 
 			if (ret == skb->len)
 				dev_kfree_skb(skb);
 		}
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- linux-2.2.18-pre15/drivers/isdn/isdn_common.h	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_common.h	Mon Dec  3 20:34:10 2001
@@ -50,7 +50,7 @@
 extern int isdn_get_free_channel(int, int, int, int, int, char *);
 extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
 extern int register_isdn(isdn_if * i);
-extern int isdn_wildmat(char *, char *);
+extern int isdn_msncmp( const char *,  const char *);
 extern int isdn_add_channels(driver *, int, int, int);
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
 extern void isdn_dumppkt(char *, u_char *, int, int);
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- linux-2.2.18-pre15/drivers/isdn/isdn_net.c	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_net.c	Mon Dec  3 20:34:10 2001
@@ -1783,7 +1783,7 @@
  *                   would eventually match if CID was longer.
  */
 int
-isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
+isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
 {
 	char *eaz;
 	int si1;
@@ -1799,19 +1799,19 @@
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
-	if (!setup.phone[0]) {
+	if (!setup->phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
 		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
 	} else
-		strcpy(nr, setup.phone);
-	si1 = (int) setup.si1;
-	si2 = (int) setup.si2;
-	if (!setup.eazmsn[0]) {
+		strcpy(nr, setup->phone);
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
 		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
 	} else
-		eaz = setup.eazmsn;
+		eaz = setup->eazmsn;
 	if (dev->net_verbose > 1)
 		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
 	/* Accept only calls with Si1 = 7 (Data-Transmission) */
@@ -1842,7 +1842,7 @@
 				break;
 		}
 		swapped = 0;
-		if (!(matchret = isdn_wildmat(eaz, isdn_map_eaz2msn(lp->msn, di))))
+		if (!(matchret = isdn_msncmp(eaz, isdn_map_eaz2msn(lp->msn, di))))
 			ematch = 1;
 		/* Remember if more numbers eventually can match */
 		if (matchret > wret)
@@ -1934,7 +1934,7 @@
 			n = lp->phone[0];
 			if (lp->flags & ISDN_NET_SECURE) {
 				while (n) {
-					if (!isdn_wildmat(nr, n->num))
+					if (!isdn_msncmp(nr, n->num))
 						break;
 					n = (isdn_net_phone *) n->next;
 				}
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- linux-2.2.18-pre15/drivers/isdn/isdn_net.h	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_net.h	Mon Dec  3 20:34:10 2001
@@ -75,7 +75,7 @@
 extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
 extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
 extern int isdn_net_delphone(isdn_net_ioctl_phone *);
-extern int isdn_net_find_icall(int, int, int, setup_parm);
+extern int isdn_net_find_icall(int, int, int, setup_parm *);
 extern void isdn_net_hangup(struct device *);
 extern void isdn_net_dial(void);
 extern void isdn_net_autohup(void);
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- linux-2.2.18-pre15/drivers/isdn/isdn_tty.c	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_tty.c	Mon Dec  3 20:34:10 2001
@@ -2152,7 +2152,7 @@
 		while (1) {
 			if ((q = strchr(p, ';')))
 				*q = '\0';
-			if ((tmp = isdn_wildmat(cid, isdn_map_eaz2msn(p, di))) > ret)
+			if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
 				ret = tmp;
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
@@ -2171,7 +2171,7 @@
 		return ret;
 	} else {
 		int tmp;
-		tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+		tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
 			       isdn_map_eaz2msn(emu->msn, di), tmp);
@@ -2192,7 +2192,7 @@
  *      CID is longer.
  */
 int
-isdn_tty_find_icall(int di, int ch, setup_parm setup)
+isdn_tty_find_icall(int di, int ch, setup_parm *setup)
 {
 	char *eaz;
 	int i;
@@ -2203,18 +2203,18 @@
 	char *nr;
 	ulong flags;
 
-	if (!setup.phone[0]) {
+	if (!setup->phone[0]) {
 		nr = "0";
 		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
 	} else
-		nr = setup.phone;
-	si1 = (int) setup.si1;
-	si2 = (int) setup.si2;
-	if (!setup.eazmsn[0]) {
+		nr = setup->phone;
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
 		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
 	} else
-		eaz = setup.eazmsn;
+		eaz = setup->eazmsn;
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
@@ -2256,8 +2256,8 @@
 					strcpy(dev->num[idx], nr);
 					strcpy(info->emu.cpn, eaz);
 					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
-					info->emu.mdmreg[REG_PLAN] = setup.plan;
-					info->emu.mdmreg[REG_SCREEN] = setup.screen;
+					info->emu.mdmreg[REG_PLAN] = setup->plan;
+					info->emu.mdmreg[REG_SCREEN] = setup->screen;
 					isdn_info_update();
 					restore_flags(flags);
 					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
diff -urN linux-2.2.18-pre15/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- linux-2.2.18-pre15/drivers/isdn/isdn_tty.h	Mon Dec  3 20:33:29 2001
+++ linux/drivers/isdn/isdn_tty.h	Mon Dec  3 20:34:10 2001
@@ -113,7 +113,7 @@
 extern void isdn_tty_modem_xmit(void);
 extern int isdn_tty_modem_init(void);
 extern void isdn_tty_readmodem(void);
-extern int isdn_tty_find_icall(int, int, setup_parm);
+extern int isdn_tty_find_icall(int, int, setup_parm *);
 extern void isdn_tty_cleanup_xmit(modem_info *);
 extern int isdn_tty_stat_callback(int, isdn_ctrl *);
 extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
diff -urN linux-2.2.18-pre15/drivers/net/Config.in linux/drivers/net/Config.in
--- linux-2.2.18-pre15/drivers/net/Config.in	Mon Dec  3 20:33:29 2001
+++ linux/drivers/net/Config.in	Mon Dec  3 20:34:10 2001
@@ -319,6 +319,7 @@
    dep_tristate '  Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
 fi
 
+
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Generic HDLC driver (EXPERIMENTAL)' CONFIG_HDLC
   if [ "$CONFIG_HDLC" != "n" ]; then
@@ -361,6 +362,11 @@
     fi 
   fi
 fi
+
+#
+# Xpeed drivers
+#
+tristate 'Xpeed X200/X300 DSL NIC support' CONFIG_XPEED
 
 endmenu
 
diff -urN linux-2.2.18-pre15/drivers/net/Makefile linux/drivers/net/Makefile
--- linux-2.2.18-pre15/drivers/net/Makefile	Mon Dec  3 20:33:29 2001
+++ linux/drivers/net/Makefile	Mon Dec  3 20:34:10 2001
@@ -112,6 +112,11 @@
 CONFIG_8390_BUILTIN = y
 endif
 
+ifeq ($(CONFIG_MAC8390), y)
+L_OBJS += mac8390.o
+CONFIG_8390_BUILTIN = y
+endif
+
 ifeq ($(CONFIG_APNE),y)
 L_OBJS += apne.o
 CONFIG_8390_BUILTIN = y
@@ -976,6 +981,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_XPEED),y)
+  SUB_DIRS += xpds
+  MOD_IN_SUB_DIRS += xpds
+  L_OBJS += xpds/xpds-fr.o
+else
+  ifeq ($(CONFIG_XPEED),m)
+    MOD_IN_SUB_DIRS += xpds
+  endif
+endif
+
 # If anything built-in uses Z85230, then build it into the kernel also.
 # If not, but a module uses it, build as a module.
 
@@ -1101,6 +1116,17 @@
   M_OBJS += hplance.o
   endif
 endif
+
+ifeq ($(CONFIG_MVME147_NET),y)
+L_OBJS += mvme147.o
+CONFIG_7990_BUILTIN = y
+else
+  ifeq ($(CONFIG_MVME147_NET),m)
+  CONFIG_7990_MODULE = y
+  M_OBJS += mvme147.o
+  endif
+endif
+
 # If we need generic LANCE support, either in the kernel or as a module,
 # build it in the appropriate way.
 ifdef CONFIG_7990_BUILTIN
@@ -1233,6 +1259,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_MAC89x0),y)
+L_OBJS += mac89x0.o
+else
+  ifeq ($(CONFIG_MAC89x0),m)
+  M_OBJS += mac89x0.o
+  endif
+endif
+
 ifeq ($(CONFIG_LTPC),y)
 L_OBJS += ltpc.o
 else
@@ -1270,6 +1304,18 @@
 else
   ifeq ($(CONFIG_MACE),m)
   M_OBJS += mace.o
+  endif
+endif
+
+ifeq ($(CONFIG_MACMACE),y)
+L_OBJS += macmace.o
+endif
+
+ifeq ($(CONFIG_MACSONIC),y)
+L_OBJS += macsonic.o
+else
+  ifeq ($(CONFIG_MACSONIC),m)
+  M_OBJS += macsonic.o
   endif
 endif
 
diff -urN linux-2.2.18-pre15/drivers/net/ariadne.c linux/drivers/net/ariadne.c
--- linux-2.2.18-pre15/drivers/net/ariadne.c	Mon Sep  4 10:39:18 2000
+++ linux/drivers/net/ariadne.c	Mon Dec  3 20:34:10 2001
@@ -399,8 +399,8 @@
 
     board->Lance.RAP = CSR0;	/* PCnet-ISA Controller Status */
 
-    if (!(board->Lance.RDP & INTR))	/* Check if any interrupt has been
-	return;				   generated by the board. */
+    if (!(board->Lance.RDP & INTR))	/* Check if any interrupt has been */
+	return;				/* generated by the board. */
 
     if (dev->interrupt)
 	printk("%s: Re-entering the interrupt handler.\n", dev->name);
diff -urN linux-2.2.18-pre15/drivers/net/ariadne2.c linux/drivers/net/ariadne2.c
--- linux-2.2.18-pre15/drivers/net/ariadne2.c	Thu Jan  7 08:41:54 1999
+++ linux/drivers/net/ariadne2.c	Mon Dec  3 20:34:10 2001
@@ -178,6 +178,7 @@
     name = "NE2000";
 
     dev->base_addr = ioaddr;
+    dev->irq = IRQ_AMIGA_PORTS;
 
     /* Install the Interrupt handler */
     if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, 0, "AriadNE2 Ethernet",
@@ -192,7 +193,9 @@
     ((struct ei_device *)dev->priv)->priv = key;
 
     for(i = 0; i < ETHER_ADDR_LEN; i++) {
+#ifdef DEBUG
 	printk(" %2.2x", SA_prom[i]);
+#endif
 	dev->dev_addr[i] = SA_prom[i];
     }
 
diff -urN linux-2.2.18-pre15/drivers/net/atari_bionet.c linux/drivers/net/atari_bionet.c
--- linux-2.2.18-pre15/drivers/net/atari_bionet.c	Sat Jun 13 13:28:34 1998
+++ linux/drivers/net/atari_bionet.c	Mon Dec  3 20:34:10 2001
@@ -446,7 +446,7 @@
 	}
 	else {
 		int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned long buf = VTOP(skb->data);
+		unsigned long buf = virt_to_phys(skb->data);
 		int stat;
 
 		stdma_lock(bionet_intr, NULL);
diff -urN linux-2.2.18-pre15/drivers/net/atari_pamsnet.c linux/drivers/net/atari_pamsnet.c
--- linux-2.2.18-pre15/drivers/net/atari_pamsnet.c	Wed Oct  7 15:51:45 1998
+++ linux/drivers/net/atari_pamsnet.c	Mon Dec  3 20:34:10 2001
@@ -439,7 +439,7 @@
 	unsigned char *buffer;
 {
 	int ret = -1;
-	unsigned char *vbuffer = (unsigned char *)PTOV(buffer);
+	unsigned char *vbuffer = phys_to_virt((unsigned long)buffer);
 	unsigned char cmd_buffer[5];
 
 	if (send_first(target, INQUIRY))
@@ -487,7 +487,7 @@
 	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
 	    get_status())
 		goto bad;
-	ret = (HADDR *)PTOV(&(((DMAHWADDR *)buffer)->hwaddr));
+	ret = phys_to_virt(&(((DMAHWADDR *)buffer)->hwaddr));
 	dma_cache_maintenance((unsigned long)buffer, 512, 0);
 bad:
 	return (ret);
@@ -707,7 +707,7 @@
 	}
 	else {
 		int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned long buf = VTOP(skb->data);
+		unsigned long buf = virt_to_phys(skb->data);
 		int stat;
 
 		stdma_lock(pamsnet_intr, NULL);
diff -urN linux-2.2.18-pre15/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- linux-2.2.18-pre15/drivers/net/daynaport.c	Thu Dec 17 09:04:49 1998
+++ linux/drivers/net/daynaport.c	Mon Dec  3 20:34:10 2001
@@ -1,4 +1,4 @@
-/* mac_ns8390.c: A Macintosh 8390 based ethernet driver for linux. */
+/* daynaport.c: A Macintosh 8390 based ethernet driver for linux. */
 /*
 	Derived from code:
 	
@@ -15,14 +15,20 @@
 	    The block output routines may be wrong for non Dayna
 	    cards
 
-	    Reading MAC addresses
-*/
+		Fix this driver so that it will attempt to use the info
+		(i.e. iobase, iosize) given to it by the new and improved
+		NuBus code.
+
+		Despite its misleading filename, this driver is not Dayna-specific
+		anymore. */
+/* Cabletron E6100 card support added by Tony Mantler (eek@escape.ca) April 1999 */
 
 static const char *version =
-	"mac_ns8390.c:v0.01 7/5/97 Alan Cox (Alan.Cox@linux.org)\n";
+	"daynaport.c: v0.02 1999-05-17 Alan Cox (Alan.Cox@linux.org) and others\n";
+static int version_printed = 0;
 
 #include <linux/module.h>
-
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
@@ -31,20 +37,26 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/hwtest.h>
+#include <asm/macints.h>
 #include <linux/delay.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include "8390.h"
 
-int ns8390_probe1(struct device *dev, int word16, char *name, int id, int prom);
+extern int console_loglevel;
+
+int ns8390_probe1(struct device *dev, int word16, char *name, int id,
+				  int prom, struct nubus_dev *ndev);
 
 static int ns8390_open(struct device *dev);
 static void ns8390_no_reset(struct device *dev);
 static int ns8390_close_card(struct device *dev);
 
+/* Interlan */
 static void interlan_reset(struct device *dev);
 
+/* Dayna */
 static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
 static void dayna_block_input(struct device *dev, int count,
@@ -52,6 +64,7 @@
 static void dayna_block_output(struct device *dev, int count,
 						   const unsigned char *buf, const int start_page);
 
+/* Sane (32-bit chunk memory read/write) */
 static void sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
 static void sane_block_input(struct device *dev, int count,
@@ -59,6 +72,7 @@
 static void sane_block_output(struct device *dev, int count,
 						   const unsigned char *buf, const int start_page);
 
+/* Slow Sane (16-bit chunk memory read/write) */
 static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
 static void slow_sane_block_input(struct device *dev, int count,
@@ -71,6 +85,10 @@
 #define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
 #define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
 
+#define CABLETRON_RX_START_PG          0x00    /* First page of RX buffer */
+#define CABLETRON_RX_STOP_PG           0x30    /* Last page +1 of RX ring */
+#define CABLETRON_TX_START_PG          CABLETRON_RX_STOP_PG  /* First page of TX buffer */
+
 
 #define DAYNA_MAC_BASE		0xf0007
 #define DAYNA_8390_BASE		0x80000 /* 3 */
@@ -81,9 +99,14 @@
 #define APPLE_8390_MEM		0xD0000
 #define APPLE_MEMSIZE		8192    /* FIXME: need to dynamically check */
 
-#define KINETICS_8390_BASE	0x80003
-#define KINETICS_8390_MEM	0x00000
+#define KINETICS_MAC_BASE	0xf0004 /* first byte of each long */
+#define KINETICS_8390_BASE	0x80000
+#define KINETICS_8390_MEM	0x00000 /* first word of each long */
 #define KINETICS_MEMSIZE	8192    /* FIXME: need to dynamically check */
+/*#define KINETICS_MEMSIZE	(0x10000/2) * CSA: on the board I have, at least */
+
+#define CABLETRON_8390_BASE		0x90000	
+#define CABLETRON_8390_MEM		0x00000
 
 static int test_8390(volatile char *ptr, int scale)
 {
@@ -113,34 +136,59 @@
  *    Identify the species of NS8390 card/driver we need
  */
 
-#define NS8390_DAYNA		1
-#define NS8390_INTERLAN		2
-#define NS8390_KINETICS		3
-#define NS8390_APPLE		4
-#define NS8390_FARALLON		5
-#define NS8390_ASANTE		6
-
-int ns8390_ident(struct nubus_type *nb)
-{
-	/* It appears anything with a software type of 0 is an apple
-	   compatible - even if the hardware matches others */
-	   
-	if(nb->DrSW==0x0001 || nb->DrSW==0x0109 || nb->DrSW==0x0000 || nb->DrSW==0x0100)
-		return NS8390_APPLE;
-	
+enum mac8390_type {
+	NS8390_DAYNA,
+	NS8390_INTERLAN,
+	NS8390_KINETICS,
+	NS8390_APPLE,
+	NS8390_FARALLON,
+	NS8390_ASANTE,
+	NS8390_CABLETRON
+};
+
+__initfunc(int ns8390_ident(struct nubus_dev* ndev))
+{
+	/* This really needs to be tested and tested hard.  */
+		
+	/* Summary of what we know so far --
+	 * SW: 0x0104 -- asante,    16 bit, back4_offsets
+	 * SW: 0x010b -- daynaport, 16 bit, fwrd4_offsets
+	 * SW: 0x010c -- farallon,  16 bit, back4_offsets, no long word access
+	 * SW: 0x011a -- focus,     [no details yet]
+	 * SW: ?????? -- interlan,  16 bit, back4_offsets, funny reset
+	 * SW: ?????? -- kinetics,   8 bit, back4_offsets
+	 * -- so i've this hypothesis going that says DrSW&1 says whether the
+	 *    map is forward or backwards -- and maybe DrSW&256 says what the
+	 *    register spacing is -- for all cards that report a DrSW in some
+	 *    range.
+	 *    This would allow the "apple compatible" driver to drive many
+	 *    seemingly different types of cards.  More DrSW info is needed
+	 *    to investigate this properly. [CSA, 21-May-1999]
+	 */
 	/* Dayna ex Kinetics board */
-	if(nb->DrHW==0x0103)
+	if(ndev->dr_sw == NUBUS_DRSW_DAYNA)
 		return NS8390_DAYNA;
-
-	/* Asante board */
-	if(nb->DrHW==0x0104)
+	if(ndev->dr_sw == NUBUS_DRSW_ASANTE)
 		return NS8390_ASANTE;
-	if(nb->DrHW==0x0100)
-		return NS8390_INTERLAN;
-	if(nb->DrHW==0x0106)
-		return NS8390_KINETICS;
-	if(nb->DrSW==0x010C)
+	if(ndev->dr_sw == NUBUS_DRSW_FARALLON) /* farallon or sonic systems */
 		return NS8390_FARALLON;
+	if(ndev->dr_sw == NUBUS_DRSW_KINETICS)
+		return NS8390_KINETICS;
+	/* My ATI Engineering card with this combination crashes the */
+	/* driver trying to xmit packets. Best not touch it for now. */
+	/*     - 1999-05-20 (funaho@jurai.org)                       */
+	if(ndev->dr_sw == NUBUS_DRSW_FOCUS)
+		return -1;
+
+	/* Check the HW on this one, because it shares the same DrSW as
+	   the on-board SONIC chips */
+	if(ndev->dr_hw == NUBUS_DRHW_CABLETRON)
+		return NS8390_CABLETRON;
+	/* does anyone have one of these? */
+	if(ndev->dr_hw == NUBUS_DRHW_INTERLAN)
+		return NS8390_INTERLAN;
+
+	/* FIXME: what do genuine Apple boards look like? */
 	return -1;
 }
 
@@ -148,7 +196,7 @@
  *	Memory probe for 8390 cards
  */
  
-int apple_8390_mem_probe(volatile unsigned short *p)
+__initfunc(int apple_8390_mem_probe(volatile unsigned short *p))
 {
 	int i, j;
 	/*
@@ -192,61 +240,79 @@
 /*
  *    Probe for 8390 cards.  
  *    The ns8390_probe1() routine initializes the card and fills the
- *    station address field. On entry base_addr is set, irq is set
- *    (These come from the nubus probe code). dev->mem_start points
+ *    station address field.
+ *
+ *    The NuBus interface has changed!  We now scan for these somewhat
+ *    like how the PCI and Zorro drivers do.  It's not clear whether
+ *    this is actually better, but it makes things more consistent.
+ *
+ *    dev->mem_start points
  *    at the memory ring, dev->mem_end gives the end of it.
  */
 
-int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type *match)
+__initfunc(int mac8390_probe(struct device *dev))
 {
-	struct device *dev;
+	static int slots = 0;
 	volatile unsigned short *i;
 	volatile unsigned char *p;
 	int plen;
 	int id;
+	static struct nubus_dev* ndev = NULL;
 
-	if(match->category!=NUBUS_CAT_NETWORK || match->type!=1)
-		return -ENODEV;		
-	/* Ok so it is an ethernet network device */
-	if((id=ns8390_ident(match))==-1)
-	{
-		printk("Ethernet but type unknown %d\n",match->DrHW);
+	/* Find the first card that hasn't already been seen */
+	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
+								   NUBUS_TYPE_ETHERNET, ndev)) != NULL) {
+		/* Have we seen it already? */
+		if (slots & (1<<ndev->board->slot))
+			continue;
+		slots |= 1<<ndev->board->slot;
+
+		/* Is it one of ours? */
+		if ((id = ns8390_ident(ndev)) != -1)
+			break;
+	}
+
+	/* Hm.  No more cards, then */
+	if (ndev == NULL)
 		return -ENODEV;
+
+	dev = init_etherdev(dev, 0);
+
+	if (!version_printed) {
+		printk(KERN_INFO "%s", version);
+		version_printed = 1;
 	}
-	dev = init_etherdev(0, 0);
-	if(dev==NULL)
-		return -ENOMEM;
 
 	/*
 	 *	Dayna specific init
 	 */
 	if(id==NS8390_DAYNA)
 	{
-		dev->base_addr=(int)(nubus_slot_addr(slot)+DAYNA_8390_BASE);
-		dev->mem_start=(int)(nubus_slot_addr(slot)+DAYNA_8390_MEM);
-		dev->mem_end=dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */
+		dev->base_addr = (int)(ndev->board->slot_addr+DAYNA_8390_BASE);
+		dev->mem_start = (int)(ndev->board->slot_addr+DAYNA_8390_MEM);
+		dev->mem_end = dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */
 	
-		printk("daynaport: testing board: ");
-
+		printk(KERN_INFO "%s: daynaport. testing board: ", dev->name);
+			
 		printk("memory - ");	
-	
-		i=(void *)dev->mem_start;
+			
+		i = (void *)dev->mem_start;
 		memset((void *)i,0xAA, DAYNA_MEMSIZE);
 		while(i<(volatile unsigned short *)dev->mem_end)
 		{
 			if(*i!=0xAAAA)
 				goto membad;
-			*i=0x5555;
-			if(*i!=0x5555)
+			*i=0x5678; /* make sure we catch byte smearing */
+			if(*i!=0x5678)
 				goto membad;
 			i+=2;	/* Skip a word */
 		}
-
+			
 		printk("controller - ");
-	
+			
 		p=(void *)dev->base_addr;
 		plen=0;
-	
+			
 		while(plen<0x3FF00)
 		{
 			if(test_8390(p,0)==0)
@@ -263,26 +329,71 @@
 		if(plen==0x3FF00)
 			goto membad;
 		printk("OK\n");
-		dev->irq=slot;
-		if(ns8390_probe1(dev, 0, "dayna", id, -1)==0)
-		return 0;
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+		if(ns8390_probe1(dev, 0, "dayna", id, -1, ndev)==0)
+			return 0;
+	}
+	/* Cabletron */
+	if (id==NS8390_CABLETRON) {
+		int memsize = 16<<10; /* fix this */
+		  
+		dev->base_addr=(int)(ndev->board->slot_addr+CABLETRON_8390_BASE);
+		dev->mem_start=(int)(ndev->board->slot_addr+CABLETRON_8390_MEM);
+		dev->mem_end=dev->mem_start+memsize;
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+		  
+		/* The base address is unreadable if 0x00 has been written to the command register */
+		/* Reset the chip by writing E8390_NODMA+E8390_PAGE0+E8390_STOP just to be sure */
+		i = (void *)dev->base_addr;
+		*i = 0x21;
+		  
+		printk(KERN_INFO "%s: cabletron: testing board: ", dev->name);
+		printk("%dK memory - ", memsize>>10);		
+		i=(void *)dev->mem_start;
+		while(i<(volatile unsigned short *)(dev->mem_start+memsize))
+		{
+			*i=0xAAAA;
+			if(*i!=0xAAAA)
+				goto membad;
+			*i=0x5555;
+			if(*i!=0x5555)
+				goto membad;
+			i+=2;	/* Skip a word */
+		}
+		printk("OK\n");
+		  
+		if(ns8390_probe1(dev, 1, "cabletron", id, -1, ndev)==0)
+			return 0;
 	}
 	/* Apple, Farallon, Asante */
-	if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE)
+	if(id==NS8390_APPLE || id==NS8390_FARALLON || id==NS8390_ASANTE)
 	{
 		int memsize;
-		
-		dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE);
-		dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM);
-		
+			
+		dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE);
+		dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM);
+			
 		memsize = apple_8390_mem_probe((void *)dev->mem_start);
-		
+			
 		dev->mem_end=dev->mem_start+memsize;
-		dev->irq=slot;
-		printk("apple/clone: testing board: ");
-
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+			
+		switch(id)
+		{
+		case NS8390_FARALLON:
+			printk(KERN_INFO "%s: farallon: testing board: ", dev->name);
+			break;
+		case NS8390_ASANTE:
+			printk(KERN_INFO "%s: asante: testing board: ", dev->name);
+			break;
+		case NS8390_APPLE:
+		default:
+			printk(KERN_INFO "%s: apple/clone: testing board: ", dev->name);
+			break;
+		}
+			
 		printk("%dK memory - ", memsize>>10);		
-
+			
 		i=(void *)dev->mem_start;
 		memset((void *)i,0xAA, memsize);
 		while(i<(volatile unsigned short *)dev->mem_end)
@@ -295,51 +406,75 @@
 			i+=2;	/* Skip a word */
 		}
 		printk("OK\n");
-
-		if(id==NS8390_FARALLON)
+			
+		switch (id)
 		{
-			if(ns8390_probe1(dev, 1, "farallon", id, -1)==0)
+		case NS8390_FARALLON:
+			if(ns8390_probe1(dev, 1, "farallon", id, -1, ndev)==0)
 				return 0;
-		}
-		else
-		{
-			if(ns8390_probe1(dev, 1, "apple/clone", id, -1)==0)
-			    return 0;
+			break;
+		case NS8390_ASANTE:
+			if(ns8390_probe1(dev, 1, "asante", id, -1, ndev)==0)
+				return 0;
+			break;
+		case NS8390_APPLE:
+		default:
+			if(ns8390_probe1(dev, 1, "apple/clone", id, -1, ndev)==0)
+				return 0;
+			break;
 		}
 	}
 	/* Interlan */
 	if(id==NS8390_INTERLAN)
 	{
 		/* As apple and asante */
-		dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE);
-		dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM);
+		dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE);
+		dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM);
 		dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */
-		dev->irq=slot;
-		if(ns8390_probe1(dev, 1, "interlan", id, -1)==0)
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+		if(ns8390_probe1(dev, 1, "interlan", id, -1, ndev)==0)
 			return 0;
 	}
-	/* Kinetics */
+	/* Kinetics (Shiva Etherport) */
 	if(id==NS8390_KINETICS)
 	{
-		dev->base_addr=(int)(nubus_slot_addr(slot)+KINETICS_8390_BASE);
-		dev->mem_start=(int)(nubus_slot_addr(slot)+KINETICS_8390_MEM);
+		dev->base_addr=(int)(ndev->board->slot_addr+KINETICS_8390_BASE);
+		dev->mem_start=(int)(ndev->board->slot_addr+KINETICS_8390_MEM);
 		dev->mem_end=dev->mem_start+KINETICS_MEMSIZE; /* 8K it seems */
-		dev->irq=slot;
-		if(ns8390_probe1(dev, 0, "kinetics", id, -1)==0)
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+		if(ns8390_probe1(dev, 0, "kinetics", id, -1, ndev)==0)
 			return 0;
 	}
-	kfree(dev);
+
+	/* We should hopefully not get here */
+	printk(KERN_ERR "Probe unsucessful.\n");
 	return -ENODEV;
-membad:
-	printk("failed.\n");
-	kfree(dev);
+
+ membad:
+	printk(KERN_ERR "failed at %p in %p - %p.\n", i,
+		   (void *)dev->mem_start, (void *)dev->mem_end);
 	return -ENODEV;
 }
 
-int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, int promoff)
+__initfunc(int mac8390_ethernet_addr(struct nubus_dev* ndev,
+									 unsigned char addr[6]))
 {
-	static unsigned version_printed = 0;
+	struct nubus_dir dir;
+	struct nubus_dirent ent;
+
+	/* Get the functional resource for this device */
+	if (nubus_get_func_dir(ndev, &dir) == -1)
+		return -1;
+	if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1)
+		return -1;
+	
+	nubus_get_rsrc_mem(addr, &ent, 6);
+	return 0;
+}
 
+__initfunc(int ns8390_probe1(struct device *dev, int word16, char *model_name,
+							 int type, int promoff, struct nubus_dev *ndev))
+{
 	static u32 fwrd4_offsets[16]={
 		0,      4,      8,      12,
 		16,     20,     24,     28,
@@ -352,25 +487,19 @@
 		28,     24,     20,     16,
 		12,     8,      4,      0
 	};
+	static u32 fwrd2_offsets[16]={
+		0,      2,      4,      6,
+		8,     10,     12,     14,
+		16,    18,     20,     22,
+		24,    26,     28,     30
+	};
 
-	unsigned char *prom=((unsigned char *)nubus_slot_addr(dev->irq))+promoff;
+	unsigned char *prom = (unsigned char*) ndev->board->slot_addr + promoff;
 
-	if (ei_debug  &&  version_printed++ == 0)
-		printk(version);
-	
-	/* Snarf the interrupt now.  There's no point in waiting since we cannot
-	   share a slot! and the board will usually be enabled. */
-	if (nubus_request_irq(dev->irq, dev, ei_interrupt)) 
-	{
-		printk (" unable to get nubus IRQ %d.\n", dev->irq);
-		return EAGAIN;
-	}
-	
 	/* Allocate dev->priv and fill in 8390 specific dev fields. */
 	if (ethdev_init(dev)) 
 	{	
-		printk (" unable to get memory for dev->priv.\n");
-		nubus_free_irq(dev->irq);
+		printk ("%s: unable to get memory for dev->priv.\n", dev->name);
 		return -ENOMEM;
 	}
 
@@ -378,16 +507,25 @@
 
 	ei_status.name = model_name;
 	ei_status.word16 = word16;
-	ei_status.tx_start_page = WD_START_PG;
-	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
 
-	dev->rmem_start = dev->mem_start + TX_PAGES*256;
-	ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
-	dev->rmem_end = dev->mem_end;
+       if (type==NS8390_CABLETRON) {
+               /* Cabletron card puts the RX buffer before the TX buffer */
+               ei_status.tx_start_page = CABLETRON_TX_START_PG;
+               ei_status.rx_start_page = CABLETRON_RX_START_PG;
+               ei_status.stop_page = CABLETRON_RX_STOP_PG;
+               dev->rmem_start = dev->mem_start;
+               dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
+       } else {
+               ei_status.tx_start_page = WD_START_PG;
+               ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+               ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+               dev->rmem_start = dev->mem_start + TX_PAGES*256;
+               dev->rmem_end = dev->mem_end;
+       }
 	
 	if(promoff==-1)		/* Use nubus resources ? */
 	{
-		if(nubus_ethernet_addr(dev->irq /* slot */, dev->dev_addr))
+		if(mac8390_ethernet_addr(ndev, dev->dev_addr))
 		{
 		  printk("mac_ns8390: MAC address not in resources!\n");
 		  return -ENODEV;
@@ -400,7 +538,7 @@
 		/* These should go in the end I hope */
 		if(type==NS8390_DAYNA)
 			x=2;
-		if(type==NS8390_INTERLAN)
+		if(type==NS8390_INTERLAN || type==NS8390_KINETICS)
 			x=4;
 		while(i<6)
 		{
@@ -412,12 +550,24 @@
 		}
 	}
 
-	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
-		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+	printk(KERN_INFO "%s: %s in slot %X (type %s)\n",
+		   dev->name, ndev->board->name, ndev->board->slot, model_name);
+	printk(KERN_INFO "MAC ");
+	{
+		int i;
+		for (i = 0; i < 6; i++) {
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i < 5)
+				printk(":");
+		}
+	}
+	printk(" IRQ %d, shared memory at %#lx-%#lx.\n",
+		   dev->irq, dev->mem_start, dev->mem_end-1);
 
 	switch(type)
 	{
 		case NS8390_DAYNA:      /* Dayna card */
+		case NS8390_KINETICS:   /* Kinetics --  8 bit config, but 16 bit mem */
 			/* 16 bit, 4 word offsets */
 			ei_status.reset_8390 = &ns8390_no_reset;
 			ei_status.block_input = &dayna_block_input;
@@ -425,6 +575,15 @@
 			ei_status.get_8390_hdr = &dayna_get_8390_hdr;
 			ei_status.reg_offset = fwrd4_offsets;
 			break;
+		case NS8390_CABLETRON: /* Cabletron */
+			/*		16 bit card, register map is short forward */
+			ei_status.reset_8390 = &ns8390_no_reset;
+			/* Ctron card won't accept 32bit values read or written to it */
+			ei_status.block_input = &slow_sane_block_input;
+			ei_status.block_output = &slow_sane_block_output;
+			ei_status.get_8390_hdr = &slow_sane_get_8390_hdr;
+			ei_status.reg_offset = fwrd2_offsets;
+			break;
 		case NS8390_FARALLON:
 		case NS8390_APPLE:	/* Apple/Asante/Farallon */
 			/*      16 bit card, register map is reversed */
@@ -450,6 +609,8 @@
 			ei_status.get_8390_hdr = &sane_get_8390_hdr;
 			ei_status.reg_offset = back4_offsets;
 			break;
+#if 0 /* i think this suffered code rot.  my kinetics card has much
+	   * different settings.  -- CSA [22-May-1999] */
 		case NS8390_KINETICS:   /* Kinetics */
 			/*      8bit card, map is forward */
 			ei_status.reset_8390 = &ns8390_no_reset;
@@ -458,6 +619,7 @@
 			ei_status.get_8390_hdr = &sane_get_8390_hdr;
 			ei_status.reg_offset = back4_offsets;
 			break;
+#endif
 		default:
 			panic("Detected a card I can't drive - whoops\n");
 	}
@@ -472,6 +634,19 @@
 static int ns8390_open(struct device *dev)
 {
 	ei_open(dev);
+
+	/* At least on my card (a Focus Enhancements PDS card) I start */
+	/* getting interrupts right away, so the driver needs to be    */
+	/* completely initialized before enabling the interrupt.        */
+	/*                             - funaho@jurai.org (1999-05-17) */
+
+	/* Non-slow interrupt, works around issues with the SONIC driver */
+	if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) 
+	{
+		printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+		return EAGAIN;
+	}
+	
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -489,24 +664,19 @@
 {
 	if (ei_debug > 1)
 		printk("%s: Shutting down ethercard.\n", dev->name);
+	free_irq(dev->irq, dev);
 	ei_close(dev);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
-struct nubus_device_specifier nubus_8390={
-	ns8390_probe,
-	NULL
-};
-
-
 /*
  *    Interlan Specific Code Starts Here
  */
 
 static void interlan_reset(struct device *dev)
 {
-	unsigned char *target=nubus_slot_addr(dev->irq);
+	unsigned char *target=nubus_slot_addr(IRQ2SLOT(dev->irq));
 	if (ei_debug > 1) 
 		printk("Need to reset the NS8390 t=%lu...", jiffies);
 	ei_status.txing = 0;
@@ -531,16 +701,23 @@
    The only complications are that the ring buffer wraps.
 */
 
-static void dayna_cpu_memcpy(struct device *dev, void *to, int from, int count)
+static void dayna_memcpy_fromcard(struct device *dev, void *to, int from, int count)
 {
 	volatile unsigned short *ptr;
 	unsigned short *target=to;
 	from<<=1;	/* word, skip overhead */
 	ptr=(unsigned short *)(dev->mem_start+from);
+	/*
+	 * Leading byte?
+	 */
+	if (from&2) {
+		*((char *)target)++ = *(((char *)ptr++)-1);
+		count--;
+	}
 	while(count>=2)
 	{
 		*target++=*ptr++;	/* Copy and */
-		ptr++;			/* Cruft and */
+		ptr++;			/* skip cruft */
 		count-=2;
 	}
 	/*
@@ -554,16 +731,24 @@
 	}
 }
 
-static void cpu_dayna_memcpy(struct device *dev, int to, const void *from, int count)
+static void dayna_memcpy_tocard(struct device *dev, int to, const void *from, int count)
 {
 	volatile unsigned short *ptr;
 	const unsigned short *src=from;
 	to<<=1;	/* word, skip overhead */
 	ptr=(unsigned short *)(dev->mem_start+to);
+	/*
+	 * Leading byte?
+	 */
+	if (to&2) { /* avoid a byte write (stomps on other data) */
+		ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++;
+		ptr++;
+		count--;
+	}
 	while(count>=2)
 	{
 		*ptr++=*src++;		/* Copy and */
-		ptr++;			/* Cruft and */
+		ptr++;			/* skip cruft */
 		count-=2;
 	}
 	/*
@@ -573,14 +758,15 @@
 	{
 		/* Big endian */
 		unsigned short v=*src;
-		*((char *)ptr)=v>>8;
+		/* card doesn't like byte writes */
+		*ptr=(*ptr&0x00FF)|(v&0xFF00);
 	}
 }
 
 static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 {
 	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
-	dayna_cpu_memcpy(dev, (void *)hdr, hdr_start, 4);
+	dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4);
 	/* Register endianism - fix here rather than 8390.c */
 	hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8);
 }
@@ -599,14 +785,14 @@
 	{
 		/* We must wrap the input move. */
 		int semi_count = dev->rmem_end - xfer_start;
-		dayna_cpu_memcpy(dev, skb->data, xfer_base, semi_count);
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
 		count -= semi_count;
-		dayna_cpu_memcpy(dev, skb->data + semi_count, 
+		dayna_memcpy_fromcard(dev, skb->data + semi_count, 
 			dev->rmem_start - dev->mem_start, count);
 	}
 	else
 	{
-		dayna_cpu_memcpy(dev, skb->data, xfer_base, count);
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
 	}
 }
 
@@ -615,7 +801,7 @@
 {
 	long shmem = (start_page - WD_START_PG)<<8;
 	
-	cpu_dayna_memcpy(dev, shmem, buf, count);
+	dayna_memcpy_tocard(dev, shmem, buf, count);
 }
 
 /*
@@ -739,6 +925,7 @@
  * Local variables:
  *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c daynaport.c"
  *  version-control: t
+ *  c-basic-offset: 4
  *  tab-width: 4
  *  kept-new-versions: 5
  * End:
diff -urN linux-2.2.18-pre15/drivers/net/mac8390.c linux/drivers/net/mac8390.c
--- linux-2.2.18-pre15/drivers/net/mac8390.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/mac8390.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,612 @@
+/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
+   Ethernet cards on Linux */
+/* Based on the former daynaport.c driver, by Alan Cox.  Some code
+   taken from or inspired by skeleton.c by Donald Becker, acenic.c by
+   Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker.
+
+   This software may be used and distributed according to the terms of
+   the GNU Public License, incorporated herein by reference.  */
+
+/* 2000-02-28: support added for Dayna and Kinetics cards by 
+   A.G.deWijn@phys.uu.nl */
+
+#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/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/nubus.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/hwtest.h>
+#include <asm/macints.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "8390.h"
+
+#if (LINUX_VERSION_CODE < 0x02030e)
+#define net_device device
+#endif
+
+#define WD_START_PG			0x00	/* First page of TX buffer */
+#define CABLETRON_RX_START_PG		0x00    /* First page of RX buffer */
+#define CABLETRON_RX_STOP_PG		0x30    /* Last page +1 of RX ring */
+#define CABLETRON_TX_START_PG		CABLETRON_RX_STOP_PG  /* First page of TX buffer */
+
+/* Unfortunately it seems we have to hardcode these for the moment */
+/* Shouldn't the card know about this? Does anyone know where to read it off the card? Do we trust the data provided by the card? */
+#define APPLE_8390_BASE		0xE0000
+
+#define DAYNA_8390_BASE		0x80000
+#define DAYNA_8390_MEM		0x00000
+
+#define KINETICS_8390_BASE	0x80000
+#define KINETICS_8390_MEM	0x00000
+
+#define CABLETRON_8390_BASE	0x90000	
+
+enum mac8390_type {
+	MAC8390_NONE = -1,
+	MAC8390_APPLE,
+	MAC8390_ASANTE,
+	MAC8390_FARALLON,  /* Apple, Asante, and Farallon are all compatible */
+	MAC8390_CABLETRON,
+	MAC8390_DAYNA,
+	MAC8390_INTERLAN,
+	MAC8390_KINETICS,
+	MAC8390_FOCUS,
+	MAC8390_SONICSYS
+};
+
+static const char * cardname[] = {
+	"apple",
+	"asante",
+	"farallon",
+	"cabletron",
+	"dayna",
+	"interlan",
+	"kinetics",
+	"focus",
+	"sonic systems"
+};
+
+static int word16[] = {
+	1, /* apple */
+	1, /* asante */
+	1, /* farallon */
+	1, /* cabletron */
+	0, /* dayna */
+	1, /* interlan */
+	0, /* kinetics */
+	1, /* focus (??) */
+	1  /* sonic systems (??) */
+};
+
+/* on which cards do we use NuBus resources? */
+static int useresources[] = {
+	1, /* apple */
+	1, /* asante */
+	1, /* farallon */
+	0, /* cabletron */
+	0, /* dayna */
+	0, /* interlan */
+	0, /* kinetics */
+	0, /* focus (??) */
+	0  /* sonic systems (??) */
+};
+
+static const char __initdata * version =
+	"mac8390.c: v0.2 2000-03-25 David Huggins-Daines <dhd@debian.org> and others\n";
+		
+extern int mac8390_probe(struct net_device * dev);
+extern enum mac8390_type mac8390_ident(struct nubus_dev * dev);
+extern int mac8390_memsize(unsigned long membase);
+extern int mac8390_promaddr(struct net_device * dev);
+extern int mac8390_memtest(struct net_device * dev);
+extern int mac8390_initdev(struct net_device * dev, struct nubus_dev * ndev,
+			   enum mac8390_type type);
+
+static int mac8390_open(struct net_device * dev);
+static int mac8390_close(struct net_device * dev);
+static void mac8390_no_reset(struct device *dev);
+
+/* Sane (32-bit chunk memory read/write) - Apple/Asante/Farallon do this*/
+static void sane_get_8390_hdr(struct device *dev,
+			      struct e8390_pkt_hdr *hdr, int ring_page);
+static void sane_block_input(struct net_device * dev, int count,
+			     struct sk_buff * skb, int ring_offset);
+static void sane_block_output(struct net_device * dev, int count,
+			      const unsigned char * buf, const int start_page);
+
+/* dayna_memcpy to and from card */
+static void dayna_memcpy_fromcard(struct device *dev, void *to,
+				int from, int count);
+static void dayna_memcpy_tocard(struct device *dev, int to,
+			      const void *from, int count);
+
+/* Dayna - Dayna/Kinetics use this */
+static void dayna_get_8390_hdr(struct device *dev,
+			       struct e8390_pkt_hdr *hdr, int ring_page);
+static void dayna_block_input(struct device *dev, int count,
+			      struct sk_buff *skb, int ring_offset);
+static void dayna_block_output(struct device *dev, int count,
+			       const unsigned char *buf, int start_page);
+
+enum mac8390_type __init mac8390_ident(struct nubus_dev * dev)
+{
+	if (dev->dr_sw == NUBUS_DRSW_ASANTE)
+		return MAC8390_ASANTE;
+	if (dev->dr_sw == NUBUS_DRSW_FARALLON)
+		return MAC8390_FARALLON;
+	if (dev->dr_sw == NUBUS_DRSW_KINETICS)
+		return MAC8390_KINETICS;
+	if (dev->dr_sw == NUBUS_DRSW_DAYNA)
+		return MAC8390_DAYNA;
+	return MAC8390_NONE;
+}
+
+int __init mac8390_memsize(unsigned long membase)
+{
+	unsigned long flags;
+	int i, j;
+	
+	save_flags(flags); cli();
+	/* Check up to 32K in 4K increments */
+	for (i = 0; i < 8; i++) {
+		volatile unsigned short *m = (unsigned short *) (membase + (i * 0x1000));
+
+		/* Unwriteable - we have a fully decoded card and the
+		   RAM end located */
+		if (hwreg_present(m) == 0)
+			break;
+		
+		/* write a distinctive byte */
+		*m = 0xA5A0 | i;
+		/* check that we read back what we wrote */
+		if (*m != (0xA5A0 | i))
+			break;
+
+		/* check for partial decode and wrap */
+		for (j = 0; j < i; j++) {
+			volatile unsigned short *p = (unsigned short *) (membase + (j * 0x1000));
+			if (*p != (0xA5A0 | j))
+				break;
+ 		}
+ 	}
+	restore_flags(flags);
+	/* in any case, we stopped once we tried one block too many,
+           or once we reached 32K */
+ 	return i * 0x1000;
+}
+
+int __init mac8390_promaddr(struct net_device * dev)
+{
+	/* XXX: lame, figure out how to do this eventually */
+	return -1;
+}
+
+static int probed __initdata = 0;
+
+int __init mac8390_probe(struct net_device * dev)
+{
+	int boards_found = 0;
+	int version_disp = 0;
+	struct nubus_dev * ndev = NULL;
+	
+	struct nubus_dir dir;
+	struct nubus_dirent ent;
+	int offset;
+
+	enum mac8390_type cardtype;
+
+	if (probed)
+		return -ENODEV;
+	probed++;
+
+	/* probably should check for Nubus instead */
+
+	if (!MACH_IS_MAC)
+		return -ENODEV;
+
+	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET, ndev))) {
+		
+		dev = NULL;
+
+		if ((cardtype = mac8390_ident(ndev)) == MAC8390_NONE)
+			continue;
+
+		dev = init_etherdev(dev, 0);
+		if (dev == NULL) {
+			printk(KERN_ERR "Unable to allocate etherdev"
+					"structure!\n");
+			return -ENOMEM;
+		}
+
+		if (version_disp == 0) {
+			version_disp = 1;
+			printk(version);
+		}
+
+		dev->irq = SLOT2IRQ(ndev->board->slot);
+		/* This is getting to be a habit */
+		dev->base_addr = ndev->board->slot_addr | ((ndev->board->slot&0xf) << 20);
+
+		/* Get some Nubus info - we will trust the card's idea
+		   of where its memory and registers are. */
+
+		if (nubus_get_func_dir(ndev, &dir) == -1) {
+			printk(KERN_ERR "%s: Unable to get Nubus functional"
+					" directory for slot %X!\n",
+			       dev->name, ndev->board->slot);
+			continue;
+		}
+		if (useresources[cardtype] == 1) {
+			if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS, &ent) == -1) {
+				printk(KERN_ERR "%s: Memory offset resource"
+						" for slot %X not found!\n",
+				       dev->name, ndev->board->slot);
+				continue;
+			}
+			nubus_get_rsrc_mem(&offset, &ent, 4);
+			dev->mem_start = dev->base_addr + offset;
+			/* yes, this is how the Apple driver does it */
+			dev->base_addr = dev->mem_start + 0x10000;
+			nubus_rewinddir(&dir);
+			if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH, &ent) == -1) {
+				printk(KERN_INFO "%s: Memory length resource"
+						 " for slot %X not found"
+						 ", probing\n",
+				       dev->name, ndev->board->slot);
+				offset = mac8390_memsize(dev->mem_start);
+				} else {
+					nubus_get_rsrc_mem(&offset, &ent, 4);
+				}
+			dev->mem_end = dev->mem_start + offset;
+		} else {
+			switch (cardtype) {
+				case MAC8390_KINETICS:
+				case MAC8390_DAYNA: /* it's the same */
+					dev->base_addr = 
+						(int)(ndev->board->slot_addr +
+						DAYNA_8390_BASE);
+					dev->mem_start = 
+						(int)(ndev->board->slot_addr +
+						DAYNA_8390_MEM);
+					dev->mem_end =
+						dev->mem_start +
+						mac8390_memsize(dev->mem_start);
+					break;
+				default:
+					printk(KERN_ERR "Card type %s is"
+							" unsupported, sorry\n",
+					       cardname[cardtype]);
+					return -ENODEV;
+			}
+		}
+
+		/* Get the MAC address */
+		nubus_rewinddir(&dir);
+		if ((nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent)) == -1) {
+			printk(KERN_INFO "%s: Ethernet address not in resources"
+					 ", probing\n",
+			       dev->name);
+			if ((mac8390_promaddr(dev)) == -1) {
+				printk(KERN_ERR "%s: Couldn't get MAC"
+						" address!\n",
+				       dev->name);
+				continue;
+			}
+		} else {
+				nubus_get_rsrc_mem(dev->dev_addr, &ent, 6);
+		}
+
+		/* Do the nasty 8390 stuff */
+		if (mac8390_initdev(dev, ndev, cardtype))
+			continue;
+		boards_found++;
+	}
+
+	/* We're outta here */
+	if (boards_found > 0)
+		return 0;
+	else
+		return -ENODEV;
+}
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
+MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
+#endif
+
+int init_module(void)
+{
+	if (mac8390_probe(NULL)) {
+		printk(KERN_NOTICE "mac8390.c: No useable cards found, driver NOT installed.\n");
+		return -ENODEV;
+	}
+	lock_8390_module();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	/* FIXME: should probably keep track of net_device structs
+           somewhere and unregister them here? */
+	unlock_8390_module();
+}
+
+#endif /* MODULE */
+
+int __init mac8390_initdev(struct net_device * dev, struct nubus_dev * ndev,
+			    enum mac8390_type type)
+{
+	static u32 fwrd4_offsets[16]={
+		0,      4,      8,      12,
+		16,     20,     24,     28,
+		32,     36,     40,     44,
+		48,     52,     56,     60
+	};
+	static u32 back4_offsets[16]={
+		60,     56,     52,     48,
+		44,     40,     36,     32,
+		28,     24,     20,     16,
+		12,     8,      4,      0
+	};
+/* Cabletron cards use this, but they aren't supported in mac8390.c */
+#if 0
+	static u32 fwrd2_offsets[16]={
+		0,      2,      4,      6,
+		8,     10,     12,     14,
+		16,    18,     20,     22,
+		24,    26,     28,     30
+	};
+#endif /* 0 */
+
+	/* 8390 specific init for dev - allocates dev->priv */
+	if (ethdev_init(dev)) {
+		printk(KERN_ERR "%s: Unable to allocate memory for dev->priv!\n", dev->name);
+		return -ENOMEM;
+	}
+
+	/* Now fill in our stuff */
+	dev->open = &mac8390_open;
+	dev->stop = &mac8390_close;
+
+	/* GAR, ei_status is actually a macro even though it looks global */
+	ei_status.name = cardname[type];
+	ei_status.word16 = word16[type];
+
+	/* Cabletron's TX/RX buffers are backwards */
+	if (type == MAC8390_CABLETRON) {
+               ei_status.tx_start_page = CABLETRON_TX_START_PG;
+               ei_status.rx_start_page = CABLETRON_RX_START_PG;
+               ei_status.stop_page = CABLETRON_RX_STOP_PG;
+               dev->rmem_start = dev->mem_start;
+               dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
+	} else {
+               ei_status.tx_start_page = WD_START_PG;
+               ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+               ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+               dev->rmem_start = dev->mem_start + TX_PAGES*256;
+               dev->rmem_end = dev->mem_end;
+	}
+	
+	/* Fill in model-specific information and functions */
+	switch(type) {
+	case MAC8390_APPLE:
+	case MAC8390_ASANTE:
+	case MAC8390_FARALLON:
+		/* 32 bit card, register map is reversed */
+		/* sane */
+		ei_status.reset_8390 = &mac8390_no_reset;
+		ei_status.block_input = &sane_block_input;
+		ei_status.block_output = &sane_block_output;
+		ei_status.get_8390_hdr = &sane_get_8390_hdr;
+		ei_status.reg_offset = back4_offsets;
+		break;
+	case MAC8390_DAYNA:
+	case MAC8390_KINETICS:
+		/* 16 bit memory */
+		/* dayna and similar */
+		ei_status.reset_8390 = &mac8390_no_reset;
+		ei_status.block_input = &dayna_block_input;
+		ei_status.block_output = &dayna_block_output;
+		ei_status.get_8390_hdr = &dayna_get_8390_hdr;
+		ei_status.reg_offset = fwrd4_offsets;
+		break;
+	default:
+		printk(KERN_ERR "Card type %s is unsupported, sorry\n", cardname[type]);
+		return -ENODEV;
+	}
+		
+	NS8390_init(dev, 0);
+
+	/* Good, done, now spit out some messages */
+	printk(KERN_INFO "%s: %s in slot %X (type %s)\n",
+		   dev->name, ndev->board->name, ndev->board->slot, cardname[type]);
+	printk(KERN_INFO "MAC ");
+	{
+		int i;
+		for (i = 0; i < 6; i++) {
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i < 5)
+				printk(":");
+		}
+	}
+	printk(" IRQ %d, shared memory at %#lx-%#lx.\n",
+		   dev->irq, dev->mem_start, dev->mem_end-1);
+	return 0;
+}
+
+static int mac8390_open(struct device *dev)
+{
+	ei_open(dev);
+	if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) {
+		printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+		return -EAGAIN;
+	}	
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int mac8390_close(struct device *dev)
+{
+	free_irq(dev->irq, dev);
+	ei_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static void mac8390_no_reset(struct device *dev)
+{
+	ei_status.txing = 0;
+	if (ei_debug > 1)
+		printk("reset not supported\n");
+	return;
+}
+
+/* dayna_memcpy_fromio/dayna_memcpy_toio */
+/* directly from daynaport.c by Alan Cox */
+static void dayna_memcpy_fromcard(struct device *dev, void *to, int from, int count)
+{
+	volatile unsigned short *ptr;
+	unsigned short *target=to;
+	from<<=1;	/* word, skip overhead */
+	ptr=(unsigned short *)(dev->mem_start+from);
+	/* Leading byte? */
+	if (from&2) {
+		*((char *)target)++ = *(((char *)ptr++)-1);
+		count--;
+	}
+	while(count>=2)
+	{
+		*target++=*ptr++;	/* Copy and */
+		ptr++;			/* skip cruft */
+		count-=2;
+	}
+	/* Trailing byte? */
+	if(count)
+	{
+		/* Big endian */
+		unsigned short v=*ptr;
+		*((char *)target)=v>>8;
+	}
+}
+
+static void dayna_memcpy_tocard(struct device *dev, int to, const void *from, int count)
+{
+	volatile unsigned short *ptr;
+	const unsigned short *src=from;
+	to<<=1;	/* word, skip overhead */
+	ptr=(unsigned short *)(dev->mem_start+to);
+	/* Leading byte? */
+	if (to&2) { /* avoid a byte write (stomps on other data) */
+		ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++;
+		ptr++;
+		count--;
+	}
+	while(count>=2)
+	{
+		*ptr++=*src++;		/* Copy and */
+		ptr++;			/* skip cruft */
+		count-=2;
+	}
+	/* Trailing byte? */
+	if(count)
+	{
+		/* Big endian */
+		unsigned short v=*src;
+		/* card doesn't like byte writes */
+		*ptr=(*ptr&0x00FF)|(v&0xFF00);
+	}
+}
+
+/* sane block input/output */
+static void sane_get_8390_hdr(struct device *dev,
+			      struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	memcpy_fromio((void *)hdr, (char *)dev->mem_start + hdr_start, 4);
+	/* Fix endianness */
+	hdr->count = swab16(hdr->count);
+}
+
+static void sane_block_input(struct device *dev, int count,
+			     struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base + dev->mem_start;
+
+	if (xfer_start + count > dev->rmem_end) {
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy_fromio(skb->data, (char *)dev->mem_start + xfer_base, semi_count);
+		count -= semi_count;
+		memcpy_toio(skb->data + semi_count, (char *)dev->rmem_start, count);
+	} else {
+		memcpy_fromio(skb->data, (char *)dev->mem_start + xfer_base, count);
+	}
+}
+
+static void sane_block_output(struct device *dev, int count,
+			      const unsigned char *buf, int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+	
+	memcpy_toio((char *)dev->mem_start + shmem, buf, count);
+}
+
+/* dayna block input/output */
+static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+
+	dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4);
+	/* Fix endianness */
+	hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void dayna_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	/* Note the offset math is done in card memory space which is word
+	   per long onto our space. */
+	 
+	if (xfer_start + count > dev->rmem_end) 
+	{
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
+		count -= semi_count;
+		dayna_memcpy_fromcard(dev, skb->data + semi_count, 
+			dev->rmem_start - dev->mem_start, count);
+	}
+	else
+	{
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
+	}
+}
+
+static void dayna_block_output(struct device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+	
+	dayna_memcpy_tocard(dev, shmem, buf, count);
+}
diff -urN linux-2.2.18-pre15/drivers/net/mac89x0.c linux/drivers/net/mac89x0.c
--- linux-2.2.18-pre15/drivers/net/mac89x0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/mac89x0.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,679 @@
+/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
+/*
+	Written 1996 by Russell Nelson, with reference to skeleton.c
+	written 1993-1994 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	The author may be reached at nelson@crynwr.com, Crynwr
+	Software, 11 Grant St., Potsdam, NY 13676
+
+  Changelog:
+
+  Mike Cruse        : mcruse@cti-ltd.com
+                    : Changes for Linux 2.0 compatibility. 
+                    : Added dev_id parameter in net_interrupt(),
+                    : request_irq() and free_irq(). Just NULL for now.
+
+  Mike Cruse        : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
+                    : in net_open() and net_close() so kerneld would know
+                    : that the module is in use and wouldn't eject the 
+                    : driver prematurely.
+
+  Mike Cruse        : Rewrote init_module() and cleanup_module using 8390.c
+                    : as an example. Disabled autoprobing in init_module(),
+                    : not a good thing to do to other devices while Linux
+                    : is running from all accounts.
+                    
+  Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
+
+  David Huggins-Daines <dhd@debian.org>
+  
+  Split this off into mac89x0.c, and gutted it of all parts which are
+  not relevant to the existing CS8900 cards on the Macintosh
+  (i.e. basically the Daynaport CS and LC cards).  To be precise:
+
+    * Removed all the media-detection stuff, because these cards are
+    TP-only.
+
+    * Lobotomized the ISA interrupt bogosity, because these cards use
+    a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
+
+    * Basically eliminated everything not relevant to getting the
+    cards minimally functioning on the Macintosh.
+
+  I might add that these cards are badly designed even from the Mac
+  standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
+  I/O space and NuBus interrupts for these cards, but neglected to
+  provide anything even remotely resembling a NuBus ROM.  Therefore we
+  have to probe for them in a brain-damaged ISA-like fashion.
+*/
+
+static char *version =
+"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
+
+/* ======================= configure the driver here ======================= */
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+
+/* ======================= end of configuration ======================= */
+
+
+/* Always include 'config.h' first in case the user wants to turn on
+   or override something. */
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#define PRINTK(x) printk x
+
+/*
+  Sources:
+
+	Crynwr packet driver epktisa.
+
+	Crystal Semiconductor data sheets.
+
+*/
+
+#include <linux/kernel.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/malloc.h>
+#include <linux/string.h>
+#include <linux/nubus.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/hwtest.h>
+#include <asm/macints.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "cs89x0.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+	struct net_device_stats stats;
+	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
+	char chip_revision;	/* revision letter of the chip ('A'...) */
+	int send_cmd;		/* the propercommand used to send a packet. */
+	int rx_mode;
+	int curr_rx_cfg;
+        int send_underrun;      /* keep track of how many underruns in a row we get */
+	struct sk_buff *skb;
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int mac89x0_probe(struct device *dev);
+extern void reset_chip(struct device *dev);
+static int net_open(struct device *dev);
+static int	net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void set_multicast_list(struct device *dev);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct net_device_stats *net_get_stats(struct device *dev);
+static int set_mac_address(struct device *dev, void *addr);
+
+
+/* Example routines you must write ;->. */
+#define tx_done(dev) 1
+
+/* For reading/writing registers ISA-style */
+static int inline
+readreg_io(struct device *dev, int portno)
+{
+	writew(swab16(portno), dev->base_addr + ADD_PORT);
+	return swab16(readw(dev->base_addr + DATA_PORT));
+}
+
+static void inline
+writereg_io(struct device *dev, int portno, int value)
+{
+	writew(swab16(portno), dev->base_addr + ADD_PORT);
+	writew(swab16(value), dev->base_addr + DATA_PORT);
+}
+
+/* These are for reading/writing registers in shared memory */
+static int inline
+readreg(struct device *dev, int portno)
+{
+	return swab16(readw(dev->mem_start + portno));
+}
+
+static void inline
+writereg(struct device *dev, int portno, int value)
+{
+	writew(swab16(value), dev->mem_start + portno);
+}
+
+/* Probe for the CS8900 card in slot E.  We won't bother looking
+   anywhere else until we have a really good reason to do so. */
+__initfunc(int mac89x0_probe(struct device *dev))
+{
+	static int once_is_enough = 0;
+	struct net_local *lp;
+	static unsigned version_printed = 0;
+	int i, slot;
+	unsigned rev_type = 0;
+	unsigned long ioaddr;
+	unsigned short sig;
+
+	if (once_is_enough)
+		return ENODEV;
+	once_is_enough = 1;
+
+	/* We might have to parameterize this later */
+	slot = 0xE;
+	/* Get out now if there's a real NuBus card in slot E */
+	if (nubus_find_slot(slot, NULL) != NULL)
+		return ENODEV;	
+
+	/* The pseudo-ISA bits always live at offset 0x300 (gee,
+           wonder why...) */
+	ioaddr = (unsigned long)
+		nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
+	{
+		unsigned long flags;
+		int card_present;
+		
+		save_flags(flags);
+		cli();
+		card_present = hwreg_present((void*) ioaddr+4)
+		  && hwreg_present((void*) ioaddr + DATA_PORT);
+		restore_flags(flags);
+
+		if (!card_present)
+			return ENODEV;
+	}
+
+	writew(0, ioaddr + ADD_PORT);
+	sig = readw(ioaddr + DATA_PORT);
+	if (sig != swab16(CHIP_EISA_ID_SIG))
+		return ENODEV;
+
+	/* Initialize the device structure. */
+	if (dev->priv == NULL) {
+		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+                memset(dev->priv, 0, sizeof(struct net_local));
+        }
+	lp = (struct net_local *)dev->priv;
+
+	/* Fill in the 'dev' fields. */
+	dev->base_addr = ioaddr;
+	dev->mem_start = (unsigned long) 
+		nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
+	dev->mem_end = dev->mem_start + 0x1000;
+
+	/* Turn on shared memory */
+	writereg_io(dev, PP_BusCTL, MEMORY_ON);
+
+	/* get the chip type */
+	rev_type = readreg(dev, PRODUCT_ID_ADD);
+	lp->chip_type = rev_type &~ REVISON_BITS;
+	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
+
+	/* Check the chip type and revision in order to set the correct send command
+	CS8920 revision C and CS8900 revision F can use the faster send. */
+	lp->send_cmd = TX_AFTER_381;
+	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
+		lp->send_cmd = TX_NOW;
+	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
+		lp->send_cmd = TX_NOW;
+
+	if (net_debug && version_printed++ == 0)
+		printk(version);
+
+	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx",
+	       dev->name,
+	       lp->chip_type==CS8900?'0':'2',
+	       lp->chip_type==CS8920M?"M":"",
+	       lp->chip_revision,
+	       dev->base_addr);
+
+	/* Try to read the MAC address */
+	if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
+		printk("\nmac89x0: No EEPROM, giving up now.\n");
+		return ENODEV;
+        } else {
+                for (i = 0; i < ETH_ALEN; i += 2) {
+			/* Big-endian (why??!) */
+			unsigned short s = readreg(dev, PP_IA + i);
+                        dev->dev_addr[i] = s >> 8;
+                        dev->dev_addr[i+1] = s & 0xff;
+                }
+        }
+
+	dev->irq = SLOT2IRQ(slot);
+	printk(" IRQ %d ADDR ", dev->irq);
+
+	/* print the ethernet address. */
+	for (i = 0; i < ETH_ALEN; i++)
+		printk("%2.2x%s", dev->dev_addr[i],
+		       ((i < ETH_ALEN-1) ? ":" : ""));
+
+	dev->open		= net_open;
+	dev->stop		= net_close;
+	dev->hard_start_xmit = net_send_packet;
+	dev->get_stats	= net_get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+	dev->set_mac_address = &set_mac_address;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	printk("\n");
+	return 0;
+}
+
+/* This is useful for something, but I don't know what yet. */
+__initfunc(void
+reset_chip(struct device *dev))
+{
+	int reset_start_time;
+
+	writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
+
+	/* wait 30 ms */
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(30*HZ/1000);
+
+	/* Wait until the chip is reset */
+	reset_start_time = jiffies;
+	while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
+		;
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine should set everything up anew at each open, even
+   registers that "should" only need to be set once at boot, so that
+   there is non-reboot way to recover if something goes wrong.
+   */
+static int
+net_open(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int i;
+
+	/* Disable the interrupt for now */
+	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
+
+	/* Grab the interrupt */
+	if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev))
+		return -EAGAIN;
+
+	/* Set up the IRQ - Apparently magic */
+	if (lp->chip_type == CS8900)
+		writereg(dev, PP_CS8900_ISAINT, 0);
+	else
+		writereg(dev, PP_CS8920_ISAINT, 0);
+
+	/* set the Ethernet address */
+	for (i=0; i < ETH_ALEN/2; i++)
+		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
+
+	/* Turn on both receive and transmit operations */
+	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
+
+	/* Receive only error free packets addressed to this card */
+	lp->rx_mode = 0;
+	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
+
+	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
+
+	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
+
+	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
+	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
+
+	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
+		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
+
+	/* now that we've got our act together, enable everything */
+	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+        MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	if (dev->tbusy) {
+		/* If we get here, some higher level has decided we are broken.
+		   There should really be a "kick me" function call instead. */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return 1;
+		if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name,
+			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
+		/* Try to restart the adaptor. */
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+	}
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	else {
+		struct net_local *lp = (struct net_local *)dev->priv;
+		unsigned long flags;
+
+		if (net_debug > 3)
+			printk("%s: sent %d byte packet of type %x\n",
+			       dev->name, skb->len,
+			       (skb->data[ETH_ALEN+ETH_ALEN] << 8)
+			       | skb->data[ETH_ALEN+ETH_ALEN+1]);
+
+		/* keep the upload from being interrupted, since we
+                   ask the chip to start transmitting before the
+                   whole packet has been completely uploaded. */
+		save_flags(flags);
+		cli();
+
+		/* initiate a transmit sequence */
+		writereg(dev, PP_TxCMD, lp->send_cmd);
+		writereg(dev, PP_TxLength, skb->len);
+
+		/* Test to see if the chip has allocated memory for the packet */
+		if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
+			/* Gasp!  It hasn't.  But that shouldn't happen since
+			   we're waiting for TxOk, so return 1 and requeue this packet. */
+			restore_flags(flags);
+			return 1;
+		}
+
+		/* Write the contents of the packet */
+		memcpy_toio(dev->mem_start + PP_TxFrame, skb->data, skb->len+1);
+
+		restore_flags(flags);
+		dev->trans_start = jiffies;
+	}
+	dev_kfree_skb (skb);
+
+	return 0;
+}
+
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct device *dev = dev_id;
+	struct net_local *lp;
+	int ioaddr, status;
+
+	if (dev == NULL) {
+		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	lp = (struct net_local *)dev->priv;
+
+	/* we MUST read all the events out of the ISQ, otherwise we'll never
+           get interrupted again.  As a consequence, we can't have any limit
+           on the number of times we loop in the interrupt handler.  The
+           hardware guarantees that eventually we'll run out of events.  Of
+           course, if you're on a slow machine, and packets are arriving
+           faster than you can read them off, you're screwed.  Hasta la
+           vista, baby!  */
+	while ((status = swab16(readw(dev->base_addr + ISQ_PORT)))) {
+		if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
+		switch(status & ISQ_EVENT_MASK) {
+		case ISQ_RECEIVER_EVENT:
+			/* Got a packet(s). */
+			net_rx(dev);
+			break;
+		case ISQ_TRANSMITTER_EVENT:
+			lp->stats.tx_packets++;
+			dev->tbusy = 0;
+			mark_bh(NET_BH);	/* Inform upper layers. */
+			if ((status & TX_OK) == 0) lp->stats.tx_errors++;
+			if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
+			if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
+			if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
+			if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+			break;
+		case ISQ_BUFFER_EVENT:
+			if (status & READY_FOR_TX) {
+				/* we tried to transmit a packet earlier,
+                                   but inexplicably ran out of buffers.
+                                   That shouldn't happen since we only ever
+                                   load one packet.  Shrug.  Do the right
+                                   thing anyway. */
+				dev->tbusy = 0;
+				mark_bh(NET_BH);	/* Inform upper layers. */
+			}
+			if (status & TX_UNDERRUN) {
+				if (net_debug > 0) printk("%s: transmit underrun\n", dev->name);
+                                lp->send_underrun++;
+                                if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
+                                else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
+                        }
+			break;
+		case ISQ_RX_MISS_EVENT:
+			lp->stats.rx_missed_errors += (status >>6);
+			break;
+		case ISQ_TX_COL_EVENT:
+			lp->stats.collisions += (status >>6);
+			break;
+		}
+	}
+	dev->interrupt = 0;
+	return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct sk_buff *skb;
+	int status, length;
+
+	status = readreg(dev, PP_RxStatus);
+	if ((status & RX_OK) == 0) {
+		lp->stats.rx_errors++;
+		if (status & RX_RUNT) lp->stats.rx_length_errors++;
+		if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
+		if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
+			/* per str 172 */
+			lp->stats.rx_crc_errors++;
+		if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+		return;
+	}
+
+	length = readreg(dev, PP_RxLength);
+	/* Malloc up new buffer. */
+	skb = alloc_skb(length, GFP_ATOMIC);
+	if (skb == NULL) {
+		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+		lp->stats.rx_dropped++;
+		return;
+	}
+	skb->len = length;
+	skb->dev = dev;
+
+	memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length);
+
+	if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
+                                 dev->name, length,
+                                 (skb->data[ETH_ALEN+ETH_ALEN] << 8)
+				 | skb->data[ETH_ALEN+ETH_ALEN+1]);
+
+        skb->protocol=eth_type_trans(skb,dev);
+	netif_rx(skb);
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes+=skb->len;
+	return;
+}
+
+/* The inverse routine to net_open(). */
+static int
+net_close(struct device *dev)
+{
+
+	writereg(dev, PP_RxCFG, 0);
+	writereg(dev, PP_TxCFG, 0);
+	writereg(dev, PP_BufCFG, 0);
+	writereg(dev, PP_BusCTL, 0);
+
+	dev->start = 0;
+
+	free_irq(dev->irq, dev);
+
+	/* Update the statistics here. */
+
+        MOD_DEC_USE_COUNT;
+	return 0;
+
+}
+
+/* Get the current statistics.	This may be called with the card open or
+   closed. */
+static struct net_device_stats *
+net_get_stats(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	cli();
+	/* Update the statistics from the device registers. */
+	lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
+	lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
+	sti();
+
+	return &lp->stats;
+}
+
+static void set_multicast_list(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if(dev->flags&IFF_PROMISC)
+	{
+		lp->rx_mode = RX_ALL_ACCEPT;
+	}
+	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+	{
+		/* The multicast-accept list is initialized to accept-all, and we
+		   rely on higher-level filtering for now. */
+		lp->rx_mode = RX_MULTCAST_ACCEPT;
+	} 
+	else
+		lp->rx_mode = 0;
+
+	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
+
+	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
+	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
+	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
+}
+
+
+static int set_mac_address(struct device *dev, void *addr)
+{
+	int i;
+	if (dev->start)
+		return -EBUSY;
+	printk("%s: Setting MAC address to ", dev->name);
+	for (i = 0; i < 6; i++)
+		printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+	printk(".\n");
+	/* set the Ethernet address */
+	for (i=0; i < ETH_ALEN/2; i++)
+		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
+
+	return 0;
+}
+
+#ifdef MODULE
+
+static char namespace[16] = "";
+static struct device dev_cs89x0 = {
+        NULL,
+        0, 0, 0, 0,
+        0, 0,
+        0, 0, 0, NULL, NULL };
+
+static int debug=0;
+
+MODULE_PARM(debug, "i");
+
+EXPORT_NO_SYMBOLS;
+
+int
+init_module(void)
+{
+	struct net_local *lp;
+
+	net_debug = debug;
+        dev_cs89x0.name = namespace;
+        dev_cs89x0.init = mac89x0_probe;
+        dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
+	lp = (struct net_local *)dev_cs89x0.priv;
+
+        if (register_netdev(&dev_cs89x0) != 0) {
+                printk(KERN_WARNING "mac89x0.c: No card found\n");
+                return -ENXIO;
+        }
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+
+#endif
+#ifdef MODULE
+	writew(0, dev_cs89x0.base_addr + ADD_PORT);
+#endif
+#ifdef MODULE
+
+        if (dev_cs89x0.priv != NULL) {
+                /* Free up the private structure, or leak memory :-)  */
+                unregister_netdev(&dev_cs89x0);
+                kfree(dev_cs89x0.priv);
+                dev_cs89x0.priv = NULL;	/* gets re-allocated by cs89x0_probe1 */
+        }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o mac89x0.o mac89x0.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  c-indent-level: 8
+ *  tab-width: 8
+ * End:
+ *
+ */
diff -urN linux-2.2.18-pre15/drivers/net/macmace.c linux/drivers/net/macmace.c
--- linux-2.2.18-pre15/drivers/net/macmace.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/macmace.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,793 @@
+/*
+ *	Driver for the Macintosh 68K onboard MACE controller with PSC
+ *	driven DMA. The MACE driver code is derived from mace.c. The
+ *	Mac68k theory of operation is courtesy of the MacBSD wizards.
+ *
+ *	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.
+ *
+ *	Copyright (C) 1996 Paul Mackerras.
+ *	Copyright (C) 1998 Alan Cox <alan@redhat.com>
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_psc.h>
+#include "mace.h"
+
+#define N_RX_RING	8
+#define N_TX_RING	2
+#define MAX_TX_ACTIVE	1
+#define NCMDS_TX	1	/* dma commands per element in tx ring */
+#define RX_BUFLEN	(ETH_FRAME_LEN + 8)
+#define TX_TIMEOUT	HZ	/* 1 second */
+
+/* Bits in transmit DMA status */
+#define TX_DMA_ERR	0x80
+
+/* The MACE is simply wired down on a Mac68K box */
+
+#define MACE_BASE	(void *)(0x50F1C000)
+#define MACE_PROM	(void *)(0x50F08001)
+
+struct mace68k_data
+{
+	volatile struct mace *mace;
+	volatile unsigned char *tx_ring;
+	volatile unsigned char *rx_ring;
+	int dma_intr;
+	unsigned char maccc;
+	struct net_device_stats stats;
+	struct timer_list tx_timeout;
+	int timeout_active;
+	int rx_slot, rx_done;
+	int tx_slot, tx_count;
+};
+
+struct mace_frame
+{
+	u16	len;
+	u16	status;
+	u16	rntpc;
+	u16	rcvcc;
+	u32	pad1;
+	u32	pad2;
+	u8	data[1];	
+	/* And frame continues.. */
+};
+
+#define PRIV_BYTES	sizeof(struct mace68k_data)
+
+static int mace68k_open(struct device *dev);
+static int mace68k_close(struct device *dev);
+static int mace68k_xmit_start(struct sk_buff *skb, struct device *dev);
+static struct net_device_stats *mace68k_stats(struct device *dev);
+static void mace68k_set_multicast(struct device *dev);
+static void mace68k_reset(struct device *dev);
+static int mace68k_set_address(struct device *dev, void *addr);
+static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void mace68k_set_timeout(struct device *dev);
+static void mace68k_tx_timeout(unsigned long data);
+
+/*
+ *	PSC DMA engine control. As you'd expect on a macintosh its
+ *	more like a lawnmower engine supplied without instructions
+ *
+ *	The basic theory of operation appears to be as follows.
+ *
+ *	There are two sets of receive DMA registers and two sets
+ *	of transmit DMA registers. Instead of the more traditional
+ *	"ring buffer" approach the Mac68K DMA engine expects you
+ *	to be loading one chain while the other runs, and then
+ *	to flip register set. Each entry in the chain is a fixed 
+ *	length.
+ */
+
+/*
+ *	Load a receive DMA channel with a base address and ring length
+ */
+  
+static void psc_load_rxdma_base(int set, void *base)
+{
+	psc_write_word(PSC_ENETRD_CMD + set, 0x0100);
+	psc_write_long(PSC_ENETRD_ADDR + set, (u32)base);
+	psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING);
+	psc_write_word(PSC_ENETRD_CMD + set, 0x9800);
+}
+
+/*
+ *	Reset the receive DMA subsystem
+ */
+  
+static void mace68k_rxdma_reset(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mace = mp->mace;
+	u8 mcc = mace->maccc;
+	
+	/*
+	 *	Turn off receive
+	 */
+	 
+	mcc&=~ENRCV;
+	mace->maccc=mcc;
+	
+	/*
+	 *	Program the DMA
+	 */
+	
+	psc_write_word(PSC_ENETRD_CTL, 0x8800);
+	psc_load_rxdma_base(0x0, (void *)virt_to_bus(mp->rx_ring));
+	psc_write_word(PSC_ENETRD_CTL, 0x0400);
+	
+	psc_write_word(PSC_ENETRD_CTL, 0x8800);
+	psc_load_rxdma_base(0x10, (void *)virt_to_bus(mp->rx_ring));
+	psc_write_word(PSC_ENETRD_CTL, 0x0400);
+	
+	mace->maccc=mcc|ENRCV;
+	
+	psc_write_word(PSC_ENETRD_CTL, 0x9800);
+	psc_write_word(PSC_ENETRD_CTL+0x10, 0x9800);
+}
+
+/*
+ *	Reset the transmit DMA subsystem
+ */
+ 
+static void mace68k_txdma_reset(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mace = mp->mace;
+	u8 mcc = mace->maccc;
+
+	psc_write_word(PSC_ENETWR_CTL,0x8800);
+	
+	mace->maccc = mcc&~ENXMT;
+	psc_write_word(PSC_ENETWR_CTL,0x400);
+	mace->maccc = mcc;
+}
+
+/*
+ *	Disable DMA
+ */
+ 
+static void mace68k_dma_off(struct device *dev)
+{
+	psc_write_word(PSC_ENETRD_CTL, 0x8800);
+	psc_write_word(PSC_ENETRD_CTL, 0x1000);
+	psc_write_word(PSC_ENETRD_CMD, 0x1100);
+	psc_write_word(PSC_ENETRD_CMD+0x10, 0x1100);
+                                        
+	psc_write_word(PSC_ENETWR_CTL, 0x8800);
+	psc_write_word(PSC_ENETWR_CTL, 0x1000);
+	psc_write_word(PSC_ENETWR_CMD, 0x1100);
+	psc_write_word(PSC_ENETWR_CMD+0x10, 0x1100);
+}
+
+/* Bit-reverse one byte of an ethernet hardware address. */
+
+static int
+bitrev(int b)
+{
+    int d = 0, i;
+
+    for (i = 0; i < 8; ++i, b >>= 1)
+	d = (d << 1) | (b & 1);
+    return d;
+}
+
+/*
+ * 	Not really much of a probe. The hardware table tells us if this
+ *	model of Macintrash has a MACE (AV macintoshes)
+ */
+ 
+int mace68k_probe(struct device *unused)
+{
+	int j;
+	static int once=0;
+	struct mace68k_data *mp;
+	unsigned char *addr;
+	struct device *dev;
+	unsigned char checksum = 0;
+	
+	/*
+	 *	There can be only one...
+	 */
+	 
+	if (once) return -ENODEV;
+	
+	once = 1;
+
+	if (macintosh_config->ether_type != MAC_ETHER_MACE) return -ENODEV;
+
+	printk("MACE ethernet should be present ");
+	
+	dev = init_etherdev(0, PRIV_BYTES);
+	if(dev==NULL)
+	{
+		printk("no free memory.\n");
+		return -ENOMEM;
+	}		
+	mp = (struct mace68k_data *) dev->priv;
+	dev->base_addr = (u32)MACE_BASE;
+	mp->mace = (volatile struct mace *) MACE_BASE;
+	
+	printk("at 0x%p", mp->mace);
+	
+	/*
+	 *	16K RX ring and 4K TX ring should do nicely
+	 */
+
+	mp->rx_ring=(void *)__get_free_pages(GFP_KERNEL, 2);
+	mp->tx_ring=(void *)__get_free_page(GFP_KERNEL);
+	
+	printk(".");
+	
+	if(mp->tx_ring==NULL || mp->rx_ring==NULL)
+	{
+		if(mp->tx_ring)
+			free_page((u32)mp->tx_ring);
+//		if(mp->rx_ring)
+//			__free_pages(mp->rx_ring,2);
+		printk("\nNo memory for ring buffers.\n");
+		return -ENOMEM;
+	}
+
+	/* We want the receive data to be uncached. We dont care about the
+	   byte reading order */
+
+	printk(".");	
+	kernel_set_cachemode((void *)mp->rx_ring, 16384, IOMAP_NOCACHE_NONSER);	
+	
+	printk(".");	
+	/* The transmit buffer needs to be write through */
+	kernel_set_cachemode((void *)mp->tx_ring, 4096, IOMAP_WRITETHROUGH);
+
+	printk(" Ok\n");	
+	dev->irq = IRQ_MAC_MACE;
+	printk(KERN_INFO "%s: MACE at", dev->name);
+
+	/*
+	 *	The PROM contains 8 bytes which total 0xFF when XOR'd
+	 *	together. Due to the usual peculiar apple brain damage
+	 *	the bytes are spaced out in a strange boundary and the
+	 * 	bits are reversed.
+	 */
+
+	addr = (void *)MACE_PROM;
+		 
+	for (j = 0; j < 6; ++j)
+	{
+		u8 v=bitrev(addr[j<<4]);
+		checksum^=v;
+		dev->dev_addr[j] = v;
+		printk("%c%.2x", (j ? ':' : ' '), dev->dev_addr[j]);
+	}
+	for (; j < 8; ++j)
+	{
+		checksum^=bitrev(addr[j<<4]);
+	}
+	
+	if(checksum!=0xFF)
+	{
+		printk(" (invalid checksum)\n");
+		return -ENODEV;
+	}		
+	printk("\n");
+
+	memset(&mp->stats, 0, sizeof(mp->stats));
+	init_timer(&mp->tx_timeout);
+	mp->timeout_active = 0;
+
+	dev->open = mace68k_open;
+	dev->stop = mace68k_close;
+	dev->hard_start_xmit = mace68k_xmit_start;
+	dev->get_stats = mace68k_stats;
+	dev->set_multicast_list = mace68k_set_multicast;
+	dev->set_mac_address = mace68k_set_address;
+
+	ether_setup(dev);
+
+	mp = (struct mace68k_data *) dev->priv;
+	mp->maccc = ENXMT | ENRCV;
+	mp->dma_intr = IRQ_MAC_MACE_DMA;
+
+	psc_write_word(PSC_ENETWR_CTL, 0x9000);
+	psc_write_word(PSC_ENETRD_CTL, 0x9000);
+	psc_write_word(PSC_ENETWR_CTL, 0x0400);
+	psc_write_word(PSC_ENETRD_CTL, 0x0400);
+                                        	
+	mace68k_dma_off(dev);
+
+	return 0;
+}
+
+/*
+ *	Reset a MACE controller
+ */
+ 
+static void mace68k_reset(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+	int i;
+
+	/* soft-reset the chip */
+	mb->biucc = SWRST;
+	udelay(100);
+
+	mb->biucc = XMTSP_64;
+	mb->imr = 0xff;		/* disable all intrs for now */
+	i = mb->ir;
+	mb->maccc = 0;		/* turn off tx, rx */
+	mb->utr = RTRD;
+	mb->fifocc = RCVFW_64;
+	mb->xmtfc = AUTO_PAD_XMIT;	/* auto-pad short frames */
+
+	/* load up the hardware address */
+	
+	mb->iac = ADDRCHG | PHYADDR;
+	
+	while ((mb->iac & ADDRCHG) != 0);
+	
+	for (i = 0; i < 6; ++i)
+		mb->padr = dev->dev_addr[i];
+
+	/* clear the multicast filter */
+	mb->iac = ADDRCHG | LOGADDR;
+
+	while ((mb->iac & ADDRCHG) != 0);
+	
+	for (i = 0; i < 8; ++i)
+		mb->ladrf = 0;
+
+	mb->plscc = PORTSEL_GPSI + ENPLSIO;
+}
+
+/*
+ *	Load the address on a mace controller.
+ */
+ 
+static int mace68k_set_address(struct device *dev, void *addr)
+{
+	unsigned char *p = addr;
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+	int i;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* load up the hardware address */
+	mb->iac = ADDRCHG | PHYADDR;
+	while ((mb->iac & ADDRCHG) != 0);
+	
+	for (i = 0; i < 6; ++i)
+		mb->padr = dev->dev_addr[i] = p[i];
+	/* note: setting ADDRCHG clears ENRCV */
+	mb->maccc = mp->maccc;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ *	Open the Macintosh MACE. Most of this is playing with the DMA
+ *	engine. The ethernet chip is quite friendly.
+ */
+ 
+static int mace68k_open(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+
+	/* reset the chip */
+	mace68k_reset(dev);
+
+	mp->rx_done = 0;
+	mace68k_rxdma_reset(dev);
+
+	/*
+	 *	The interrupt is fixed and comes off the PSC.
+	 */
+	 
+	if (request_irq(dev->irq, mace68k_interrupt, 0, "68K MACE", dev))
+	{
+		printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
+		return -EAGAIN;
+	}
+
+	/*
+	 *	Ditto the DMA interrupt.
+	 */
+	 
+	if (request_irq(IRQ_MAC_MACE_DMA, mace68k_dma_intr, 0, "68K MACE DMA",
+			dev))
+	{
+		printk(KERN_ERR "MACE: can't get irq %d\n", IRQ_MAC_MACE_DMA);
+		return -EAGAIN;
+	}
+
+	/* Activate the Mac DMA engine */
+
+	mp->tx_slot = 0;		/* Using register set 0 */
+	mp->tx_count = 1;		/* 1 Buffer ready for use */
+	mace68k_txdma_reset(dev);
+	
+	/* turn it on! */
+	mb->maccc = mp->maccc;
+	/* enable all interrupts except receive interrupts */
+	mb->imr = RCVINT;
+	return 0;
+}
+
+/*
+ *	Shut down the mace and its interrupt channel
+ */
+ 
+static int mace68k_close(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+
+	/* disable rx and tx */
+	mb->maccc = 0;
+	mb->imr = 0xff;		/* disable all intrs */
+
+	/* disable rx and tx dma */
+
+	mace68k_dma_off(dev);
+
+	free_irq(dev->irq, dev);
+	free_irq(IRQ_MAC_MACE_DMA, dev);
+	return 0;
+}
+
+static inline void mace68k_set_timeout(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (mp->timeout_active)
+		del_timer(&mp->tx_timeout);
+	mp->tx_timeout.expires = jiffies + TX_TIMEOUT;
+	mp->tx_timeout.function = mace68k_tx_timeout;
+	mp->tx_timeout.data = (unsigned long) dev;
+	add_timer(&mp->tx_timeout);
+	mp->timeout_active = 1;
+	restore_flags(flags);
+}
+
+/*
+ *	Transmit a frame
+ */
+ 
+static int mace68k_xmit_start(struct sk_buff *skb, struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	/*
+	 *	This may need atomic types ???
+	 */
+	if(mp->tx_count == 0)
+	{
+		dev->tbusy=1;
+		return 1;
+	}
+	mp->tx_count--;
+	
+	/*
+	 *	FIXME:
+	 *	This is hackish. The memcpy probably isnt needed but
+	 *	the rules for alignment are not known. Ideally we'd like
+	 *	to just blast the skb directly to ethernet. We also don't
+	 *	use the ring properly - just a one frame buffer. That
+	 *	also requires cache pushes ;).
+	 */
+	memcpy((void *)mp->tx_ring, skb, skb->len);
+	psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, virt_to_bus(mp->tx_ring));
+        psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len);
+        psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800);                       
+	mp->stats.tx_packets++;
+	mp->stats.tx_bytes+=skb->len;
+        dev_kfree_skb(skb);
+	return 0;
+}
+
+static struct net_device_stats *mace68k_stats(struct device *dev)
+{
+	struct mace68k_data *p = (struct mace68k_data *) dev->priv;
+	return &p->stats;
+}
+
+/*
+ * CRC polynomial - used in working out multicast filter bits.
+ */
+#define CRC_POLY	0xedb88320
+
+static void mace68k_set_multicast(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+	int i, j, k, b;
+	unsigned long crc;
+
+	mp->maccc &= ~PROM;
+	if (dev->flags & IFF_PROMISC)
+	{
+		mp->maccc |= PROM;
+	} else
+	{
+		unsigned char multicast_filter[8];
+		struct dev_mc_list *dmi = dev->mc_list;
+
+		if (dev->flags & IFF_ALLMULTI)
+		{
+			for (i = 0; i < 8; i++)
+				multicast_filter[i] = 0xff;
+		} else
+		{
+			for (i = 0; i < 8; i++)
+				multicast_filter[i] = 0;
+			for (i = 0; i < dev->mc_count; i++)
+			{
+				crc = ~0;
+				for (j = 0; j < 6; ++j)
+				{
+					b = dmi->dmi_addr[j];
+					for (k = 0; k < 8; ++k)
+					{
+						if ((crc ^ b) & 1)
+							crc = (crc >> 1) ^ CRC_POLY;
+						else
+							crc >>= 1;
+						b >>= 1;
+					}
+				}
+				j = crc >> 26;	/* bit number in multicast_filter */
+				multicast_filter[j >> 3] |= 1 << (j & 7);
+				dmi = dmi->next;
+			}
+		}
+#if 0
+		printk("Multicast filter :");
+		for (i = 0; i < 8; i++)
+			printk("%02x ", multicast_filter[i]);
+		printk("\n");
+#endif
+
+		mb->iac = ADDRCHG | LOGADDR;
+		while ((mb->iac & ADDRCHG) != 0);
+		
+		for (i = 0; i < 8; ++i)
+			mb->ladrf = multicast_filter[i];
+	}
+	/* reset maccc */
+	mb->maccc = mp->maccc;
+}
+
+/*
+ *	Miscellaneous interrupts are handled here. We may end up 
+ *	having to bash the chip on the head for bad errors
+ */
+ 
+static void mace68k_handle_misc_intrs(struct mace68k_data *mp, int intr)
+{
+	volatile struct mace *mb = mp->mace;
+	static int mace68k_babbles, mace68k_jabbers;
+
+	if (intr & MPCO)
+		mp->stats.rx_missed_errors += 256;
+	mp->stats.rx_missed_errors += mb->mpc;	/* reading clears it */
+	if (intr & RNTPCO)
+		mp->stats.rx_length_errors += 256;
+	mp->stats.rx_length_errors += mb->rntpc;	/* reading clears it */
+	if (intr & CERR)
+		++mp->stats.tx_heartbeat_errors;
+	if (intr & BABBLE)
+		if (mace68k_babbles++ < 4)
+			printk(KERN_DEBUG "mace: babbling transmitter\n");
+	if (intr & JABBER)
+		if (mace68k_jabbers++ < 4)
+			printk(KERN_DEBUG "mace: jabbering transceiver\n");
+}
+
+/*
+ *	A transmit error has occured. (We kick the transmit side from
+ *	the DMA completion)
+ */
+ 
+static void mace68k_xmit_error(struct device *dev)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+	u8 xmtfs, xmtrc;
+	
+	xmtfs = mb->xmtfs;
+	xmtrc = mb->xmtrc;
+	
+	if(xmtfs & XMTSV)
+	{
+		if(xmtfs & UFLO)
+		{
+			printk("%s: DMA underrun.\n", dev->name);
+			mp->stats.tx_errors++;
+			mp->stats.tx_fifo_errors++;
+			mace68k_reset(dev);
+		}
+		if(xmtfs & RTRY)
+			mp->stats.collisions++;
+	}			
+	mark_bh(NET_BH);
+}
+
+/*
+ *	A receive interrupt occured.
+ */
+ 
+static void mace68k_recv_interrupt(struct device *dev)
+{
+//	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+//	volatile struct mace *mb = mp->mace;
+}
+
+/*
+ *	Process the chip interrupt
+ */
+ 
+static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_id;
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	volatile struct mace *mb = mp->mace;
+	u8 ir;
+	
+	ir = mb->ir;
+	mace68k_handle_misc_intrs(mp, ir);
+	
+	if(ir&XMTINT)
+		mace68k_xmit_error(dev);
+	if(ir&RCVINT)
+		mace68k_recv_interrupt(dev);
+}
+
+static void mace68k_tx_timeout(unsigned long data)
+{
+//	struct device *dev = (struct device *) data;
+//	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+//	volatile struct mace *mb = mp->mace;
+}
+
+/*
+ *	Handle a newly arrived frame
+ */
+ 
+static void mace_dma_rx_frame(struct device *dev, struct mace_frame *mf)
+{
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+	struct sk_buff *skb;
+
+	if(mf->status&RS_OFLO)
+	{
+		printk("%s: fifo overflow.\n", dev->name);
+		mp->stats.rx_errors++;
+		mp->stats.rx_fifo_errors++;
+	}
+	if(mf->status&(RS_CLSN|RS_FRAMERR|RS_FCSERR))
+		mp->stats.rx_errors++;
+		
+	if(mf->status&RS_CLSN)
+		mp->stats.collisions++;
+	if(mf->status&RS_FRAMERR)
+		mp->stats.rx_frame_errors++;
+	if(mf->status&RS_FCSERR)
+		mp->stats.rx_crc_errors++;
+		
+	skb = dev_alloc_skb(mf->len+2);
+	if(skb==NULL)
+	{
+		mp->stats.rx_dropped++;
+		return;
+	}
+	skb_reserve(skb,2);
+	memcpy(skb_put(skb, mf->len), mf->data, mf->len);
+	
+	skb->protocol = eth_type_trans(skb, dev);
+	netif_rx(skb);
+	mp->stats.rx_packets++;
+	mp->stats.rx_bytes+=mf->len;
+}
+
+/*
+ *	The PSC has passed us a DMA interrupt event.
+ */
+ 
+static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_id;
+	struct mace68k_data *mp = (struct mace68k_data *) dev->priv;
+
+	u32 psc_status;
+	
+	/* It seems this must be allowed to stabilise ?? */
+	
+	while((psc_status=psc_read_long(0x0804))!=psc_read_long(0x0804));
+
+	/*
+	 *	Was this an ethernet event ?
+	 */
+	 	
+	if(psc_status&0x60000000)
+	{
+		/*
+		 *	Process the read queue
+		 */
+		 
+		u16 psc_status = psc_read_word(PSC_ENETRD_CTL);
+		
+		if(psc_status&0x2000)
+		{
+			mace68k_rxdma_reset(dev);
+			mp->rx_done = 0;
+		}
+		
+		else if(psc_status&0x100)
+		{
+			int left;
+			
+			psc_write_word(PSC_ENETRD_CMD+mp->rx_slot, 0x1100);
+			left=psc_read_long(PSC_ENETRD_LEN+mp->rx_slot);
+			/* read packets */	
+			
+			while(mp->rx_done < left)
+			{
+				struct mace_frame *mf=((struct mace_frame *)
+					mp->rx_ring)+mp->rx_done++;
+				mace_dma_rx_frame(dev, mf);
+			}
+			
+			if(left == 0)	/* Out of DMA room */
+			{
+				psc_load_rxdma_base(mp->rx_slot, 
+					(void *)virt_to_phys(mp->rx_ring));
+				mp->rx_slot^=16;
+				mp->rx_done = 0;
+			}
+			else
+			{
+				psc_write_word(PSC_ENETRD_CMD+mp->rx_slot,
+					0x9800);
+			}
+					
+		}
+		
+		/*
+		 *	Process the write queue
+		 */
+		 
+		psc_status = psc_read_word(PSC_ENETWR_CTL);
+		if(psc_status&0x2000) {
+			mace68k_txdma_reset(dev);
+		} else if(psc_status&0x0100) {
+			psc_write_word(PSC_ENETWR_CMD+mp->tx_slot, 0x100);
+			mp->tx_slot^=16;
+			mp->tx_count++;
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+	}
+}
diff -urN linux-2.2.18-pre15/drivers/net/macsonic.c linux/drivers/net/macsonic.c
--- linux-2.2.18-pre15/drivers/net/macsonic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/macsonic.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,624 @@
+/*
+ * macsonic.c
+ *
+ * (C) 1998 Alan Cox
+ *
+ * Debugging Andreas Ehliar, Michael Schmitz
+ *
+ * Based on code
+ * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de)
+ * 
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ * 
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the Mac onboard Sonic ethernet chip.
+ *
+ * 98/12/21 MSch: judged from tests on Q800, it's basically working, 
+ *		  but eating up both receive and transmit resources
+ *		  and duplicating packets. Needs more testing.
+ *
+ * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/nubus.h>
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/hwtest.h>
+#include <asm/dma.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#define SREGS_PAD(n)    u16 n;
+
+#include "sonic.h"
+
+static int sonic_debug = 0;
+static int sonic_version_printed = 0;
+
+extern int macsonic_probe(struct device* dev);
+extern int mac_onboard_sonic_probe(struct device* dev);
+extern int mac_nubus_sonic_probe(struct device* dev);
+
+/* For onboard SONIC */
+#define ONBOARD_SONIC_REGISTERS	0x50F0A000
+#define ONBOARD_SONIC_PROM_BASE	0x50f08000
+
+enum macsonic_type {
+	MACSONIC_DUODOCK,
+	MACSONIC_APPLE,
+	MACSONIC_APPLE16,
+	MACSONIC_DAYNA,
+	MACSONIC_DAYNALINK
+};
+
+/* For the built-in SONIC in the Duo Dock */
+#define DUODOCK_SONIC_REGISTERS 0xe10000
+#define DUODOCK_SONIC_PROM_BASE 0xe12000
+
+/* For Apple-style NuBus SONIC */
+#define APPLE_SONIC_REGISTERS	0
+#define APPLE_SONIC_PROM_BASE	0x40000
+
+/* Daynalink LC SONIC */
+#define DAYNALINK_PROM_BASE 0x400000
+
+/* For Dayna-style NuBus SONIC (haven't seen one yet) */
+#define DAYNA_SONIC_REGISTERS   0x180000
+/* This is what OpenBSD says.  However, this is definitely in NuBus
+   ROM space so we should be able to get it by walking the NuBus
+   resource directories */
+#define DAYNA_SONIC_MAC_ADDR	0xffe004
+
+#define SONIC_READ_PROM(addr) readb(prom_addr+addr)
+
+__initfunc(int macsonic_probe(struct device* dev))
+{
+	int rv;
+
+	/* This will catch fatal stuff like -ENOMEM as well as success */
+	if ((rv = mac_onboard_sonic_probe(dev)) != -ENODEV)
+		return rv;
+	return mac_nubus_sonic_probe(dev);
+}
+
+/*
+ * For reversing the PROM address
+ */
+
+static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14,
+				    1, 9, 5, 13, 3, 11, 7, 15};
+
+static inline void bit_reverse_addr(unsigned char addr[6])
+{
+	int i;
+
+	for(i = 0; i < 6; i++)
+		addr[i] = ((nibbletab[addr[i] & 0xf] << 4) | 
+			   nibbletab[(addr[i] >> 4) &0xf]);
+}
+
+__initfunc(int macsonic_init(struct device* dev))
+{
+	struct sonic_local* lp = (struct sonic_local *)dev->priv;
+	int i;
+
+	/* Allocate the entire chunk of memory for the descriptors.
+           Note that this cannot cross a 64K boundary. */
+	for (i = 0; i < 20; i++) {
+		unsigned long desc_base, desc_top;
+		if ((lp->sonic_desc = 
+		     kmalloc(SIZEOF_SONIC_DESC
+			     * SONIC_BUS_SCALE(lp->dma_bitmode), GFP_DMA)) == NULL) {
+			printk(KERN_ERR "%s: couldn't allocate descriptor buffers\n", dev->name);
+		}
+		desc_base = (unsigned long) lp->sonic_desc;
+		desc_top = desc_base + SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode);
+		if ((desc_top & 0xffff) >= (desc_base & 0xffff))
+			break;
+		/* Hmm. try again (FIXME: does this actually work?) */
+		kfree(lp->sonic_desc);
+		printk(KERN_DEBUG
+		       "%s: didn't get continguous chunk [%08lx - %08lx], trying again\n",
+		       dev->name, desc_base, desc_top);
+	}
+
+	if (lp->sonic_desc == NULL) {
+		printk(KERN_ERR "%s: tried 20 times to allocate descriptor buffers, giving up.\n",
+		       dev->name);
+		return -ENOMEM;
+	}		       
+
+	/* Now set up the pointers to point to the appropriate places */
+	lp->cda = lp->sonic_desc;
+	lp->tda = lp->cda + (SIZEOF_SONIC_CDA * SONIC_BUS_SCALE(lp->dma_bitmode));
+	lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+			     * SONIC_BUS_SCALE(lp->dma_bitmode));
+	lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+			     * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+	/* FIXME, maybe we should use skbs */
+	if ((lp->rba = (char *)
+	     kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_DMA)) == NULL) {
+		printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name);
+		return -ENOMEM;
+	}
+
+	{
+		int rs, ds;
+
+		/* almost always 12*4096, but let's not take chances */
+		rs = ((SONIC_NUM_RRS * SONIC_RBSIZE + 4095) / 4096) * 4096;
+		/* almost always under a page, but let's not take chances */
+		ds = ((SIZEOF_SONIC_DESC + 4095) / 4096) * 4096;
+		kernel_set_cachemode(lp->rba, rs, IOMAP_NOCACHE_SER);
+		kernel_set_cachemode(lp->sonic_desc, ds, IOMAP_NOCACHE_SER);
+	}
+	
+	flush_cache_all();
+
+	dev->open = sonic_open;
+	dev->stop = sonic_close;
+	dev->hard_start_xmit = sonic_send_packet;
+	dev->get_stats = sonic_get_stats;
+	dev->set_multicast_list = &sonic_multicast_list;
+
+	/*
+	 * clear tally counter
+	 */
+	sonic_write(dev, SONIC_CRCT, 0xffff);
+	sonic_write(dev, SONIC_FAET, 0xffff);
+	sonic_write(dev, SONIC_MPT, 0xffff);
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+	return 0;
+}
+
+__initfunc(int mac_onboard_sonic_ethernet_addr(struct device* dev))
+{
+	const int prom_addr = ONBOARD_SONIC_PROM_BASE;
+	int i;
+
+	/* On NuBus boards we can sometimes look in the ROM resources.
+	   No such luck for comm-slot/onboard. */
+	for(i = 0; i < 6; i++)
+		dev->dev_addr[i] = SONIC_READ_PROM(i);
+
+	/* Most of the time, the address is bit-reversed.  The NetBSD
+	   source has a rather long and detailed historical account of
+	   why this is so. */
+	if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
+	    memcmp(dev->dev_addr, "\x00\xA0\x40", 3) &&
+	    memcmp(dev->dev_addr, "\x00\x05\x02", 3))
+		bit_reverse_addr(dev->dev_addr);
+	else
+		return 0;
+
+	/* If we still have what seems to be a bogus address, we'll
+           look in the CAM.  The top entry should be ours. */
+	/* Danger! This only works if MacOS has already initialized
+           the card... */
+	if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
+	    memcmp(dev->dev_addr, "\x00\xA0\x40", 3) &&
+	    memcmp(dev->dev_addr, "\x00\x05\x02", 3))
+	{
+		unsigned short val;
+
+		printk(KERN_INFO "macsonic: PROM seems to be wrong, trying CAM entry 15\n");
+		
+		sonic_write(dev, SONIC_CMD, SONIC_CR_RST);
+		sonic_write(dev, SONIC_CEP, 15);
+
+		val = sonic_read(dev, SONIC_CAP2);
+		dev->dev_addr[5] = val >> 8;
+		dev->dev_addr[4] = val & 0xff;
+		val = sonic_read(dev, SONIC_CAP1);
+		dev->dev_addr[3] = val >> 8;
+		dev->dev_addr[2] = val & 0xff;
+		val = sonic_read(dev, SONIC_CAP0);
+		dev->dev_addr[1] = val >> 8;
+		dev->dev_addr[0] = val & 0xff;
+		
+		printk(KERN_INFO "HW Address from CAM 15: ");
+		for (i = 0; i < 6; i++) {
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i < 5)
+				printk(":");
+		}
+		printk("\n");
+	} else return 0;
+
+	if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
+	    memcmp(dev->dev_addr, "\x00\xA0\x40", 3) &&
+	    memcmp(dev->dev_addr, "\x00\x05\x02", 3))
+	{
+		/*
+		 * Still nonsense ... messed up someplace!
+		 */
+		printk(KERN_ERR "macsonic: ERROR (INVALID MAC)\n");
+		return -EIO;
+	} else return 0;
+}
+
+__initfunc(int mac_onboard_sonic_probe(struct device* dev))
+{
+	/* Bwahahaha */
+	static int once_is_more_than_enough = 0;
+	struct sonic_local* lp;
+	int i;
+	
+	if (once_is_more_than_enough)
+		return -ENODEV;
+	once_is_more_than_enough = 1;
+
+	if (!MACH_IS_MAC)
+		return -ENODEV;
+
+	printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
+
+	if (macintosh_config->ether_type != MAC_ETHER_SONIC)
+	{
+		printk("none.\n");
+		return -ENODEV;
+	}
+
+	/* Bogus probing, on the models which may or may not have
+	   Ethernet (BTW, the Ethernet *is* always at the same
+	   address, and nothing else lives there, at least if Apple's
+	   documentation is to be believed) */
+	if (macintosh_config->ident == MAC_MODEL_Q630 ||
+	    macintosh_config->ident == MAC_MODEL_P588 ||
+	    macintosh_config->ident == MAC_MODEL_C610) {
+		unsigned long flags;
+		int card_present;
+
+		save_flags(flags);
+		cli();
+		card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS);
+		restore_flags(flags);
+
+		if (!card_present) {
+			printk("none.\n");
+			return -ENODEV;
+		}
+	}
+
+	printk("yes\n");	
+
+	if (dev) {
+		dev = init_etherdev(dev, sizeof(struct sonic_local));
+		/* methinks this will always be true but better safe than sorry */
+		if (dev->priv == NULL)
+			dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL);
+	} else {
+		dev = init_etherdev(NULL, sizeof(struct sonic_local));
+	}
+
+	if (dev == NULL)
+		return -ENOMEM;
+
+	lp = (struct sonic_local*) dev->priv;
+	memset(lp, 0, sizeof(struct sonic_local));
+	/* Danger!  My arms are flailing wildly!  You *must* set this
+           before using sonic_read() */
+
+	dev->base_addr = ONBOARD_SONIC_REGISTERS;
+	if (via_alt_mapping)
+		dev->irq = IRQ_AUTO_3;
+	else
+		dev->irq = IRQ_NUBUS_9;
+
+	if (!sonic_version_printed) {
+		printk(KERN_INFO "%s", version);
+		sonic_version_printed = 1;
+	}
+	printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n",
+	       dev->name, dev->base_addr);
+
+	/* Now do a song and dance routine in an attempt to determine
+           the bus width */
+
+	/* The PowerBook's SONIC is 16 bit always. */
+	if (macintosh_config->ident == MAC_MODEL_PB520) {
+		lp->reg_offset = 0;
+		lp->dma_bitmode = 0;
+	} else {
+		/* Some of the comm-slot cards are 16 bit.  But some
+                   of them are not.  The 32-bit cards use offset 2 and
+                   pad with zeroes or sometimes ones (I think...)
+                   Therefore, if we try offset 0 and get a silicon
+                   revision of 0, we assume 16 bit. */
+		int sr;
+
+		/* Technically this is not necessary since we zeroed
+                   it above */
+		lp->reg_offset = 0;
+		lp->dma_bitmode = 0;
+		sr = sonic_read(dev, SONIC_SR);
+		if (sr == 0 || sr == 0xffff) {
+			lp->reg_offset = 2;
+			/* 83932 is 0x0004, 83934 is 0x0100 or 0x0101 */
+			sr = sonic_read(dev, SONIC_SR);
+			lp->dma_bitmode = 1;
+			
+		}
+		printk(KERN_INFO
+		       "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
+		       dev->name, sr, lp->dma_bitmode?32:16, lp->reg_offset);
+	}
+
+
+	/* Software reset, then initialize control registers. */
+	sonic_write(dev, SONIC_CMD, SONIC_CR_RST);
+	sonic_write(dev, SONIC_DCR, SONIC_DCR_BMS |
+		    SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS |
+		    (lp->dma_bitmode ? SONIC_DCR_DW : 0));
+
+	/* Clear *and* disable interrupts to be on the safe side */
+	sonic_write(dev, SONIC_ISR,0x7fff);
+	sonic_write(dev, SONIC_IMR,0);
+
+	/* Now look for the MAC address. */
+	if (mac_onboard_sonic_ethernet_addr(dev) != 0)
+		return -ENODEV;
+
+	printk(KERN_INFO "MAC ");
+	for (i = 0; i < 6; i++) {
+		printk("%2.2x", dev->dev_addr[i]);
+		if (i < 5)
+			printk(":");
+	}
+
+	printk(" IRQ %d\n", dev->irq);
+
+	/* Shared init code */
+	return macsonic_init(dev);
+}
+
+__initfunc(int mac_nubus_sonic_ethernet_addr(struct device* dev,
+					     unsigned long prom_addr,
+					     int id))
+{
+	int i;
+	for(i = 0; i < 6; i++)
+		dev->dev_addr[i] = SONIC_READ_PROM(i);
+	/* For now we are going to assume that they're all bit-reversed */
+	bit_reverse_addr(dev->dev_addr);
+
+	return 0;
+}
+
+__initfunc(int macsonic_ident(struct nubus_dev* ndev))
+{
+	if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && 
+	    ndev->dr_sw == NUBUS_DRSW_SONIC_LC)
+		return MACSONIC_DAYNALINK;
+	if (ndev->dr_hw == NUBUS_DRHW_SONIC &&
+	    ndev->dr_sw == NUBUS_DRSW_APPLE) {
+		/* There has to be a better way to do this... */
+		if (strstr(ndev->board->name, "DuoDock"))
+			return MACSONIC_DUODOCK;
+		else
+			return MACSONIC_APPLE;
+	}
+	if (ndev->dr_hw == NUBUS_DRHW_APPLE_SONIC_LC &&
+	    ndev->dr_sw == 0) { /* huh? */
+		return MACSONIC_APPLE16;
+	}
+	return -1;
+}
+
+__initfunc(int mac_nubus_sonic_probe(struct device* dev))
+{
+	static int slots = 0;
+	struct nubus_dev* ndev = NULL;
+	struct sonic_local* lp;
+	unsigned long base_addr, prom_addr;
+	u16 sonic_dcr;
+	int id = -1;
+	int i;
+	int reg_offset, dma_bitmode;
+
+	/* Find the first SONIC that hasn't been initialized already */
+	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
+				       NUBUS_TYPE_ETHERNET, ndev)) != NULL)
+	{
+		/* Have we seen it already? */
+		if (slots & (1<<ndev->board->slot))
+			continue;
+		slots |= 1<<ndev->board->slot;
+
+		/* Is it one of ours? */
+		if ((id = macsonic_ident(ndev)) != -1)
+			break;
+	}
+
+	if (ndev == NULL)
+		return -ENODEV;
+
+	switch (id) {
+	case MACSONIC_DUODOCK:
+		base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS;
+		prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE;
+		sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1
+			| SONIC_DCR_TFT0;
+		reg_offset = 2;
+		dma_bitmode = 1;
+		break;
+	case MACSONIC_APPLE:
+		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+		prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+		sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0;
+		reg_offset = 0;
+		dma_bitmode = 1;
+		break;
+	case MACSONIC_APPLE16:
+		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+		prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+		sonic_dcr = SONIC_DCR_EXBUS
+ 			| SONIC_DCR_RFT1 | SONIC_DCR_TFT0
+			| SONIC_DCR_PO1 | SONIC_DCR_BMS; 
+		reg_offset = 0;
+		dma_bitmode = 0;
+		break;
+	case MACSONIC_DAYNALINK:
+		base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+		prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE;
+		sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0
+			| SONIC_DCR_PO1 | SONIC_DCR_BMS; 
+		reg_offset = 0;
+		dma_bitmode = 0;
+		break;
+	case MACSONIC_DAYNA:
+		base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS;
+		prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR;
+		sonic_dcr = SONIC_DCR_BMS
+			| SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1;
+		reg_offset = 0;
+		dma_bitmode = 0;
+		break;
+	default:
+		printk(KERN_ERR "macsonic: WTF, id is %d\n", id);
+		return -ENODEV;
+	}
+
+	if (dev) {
+		dev = init_etherdev(dev, sizeof(struct sonic_local));
+		/* methinks this will always be true but better safe than sorry */
+		if (dev->priv == NULL)
+			dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL);
+	} else {
+		dev = init_etherdev(NULL, sizeof(struct sonic_local));
+	}
+
+	if (dev == NULL)
+		return -ENOMEM;
+
+	lp = (struct sonic_local*) dev->priv;
+	memset(lp, 0, sizeof(struct sonic_local));
+	/* Danger!  My arms are flailing wildly!  You *must* set this
+           before using sonic_read() */
+	lp->reg_offset = reg_offset;
+	lp->dma_bitmode = dma_bitmode;
+	dev->base_addr = base_addr;
+	dev->irq = SLOT2IRQ(ndev->board->slot);
+
+	if (!sonic_version_printed) {
+		printk(KERN_INFO "%s", version);
+		sonic_version_printed = 1;
+	}
+	printk(KERN_INFO "%s: %s in slot %X\n",
+	       dev->name, ndev->board->name, ndev->board->slot);
+	printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
+	       dev->name, sonic_read(dev, SONIC_SR), dma_bitmode?32:16, reg_offset);
+
+	/* Software reset, then initialize control registers. */
+	sonic_write(dev, SONIC_CMD, SONIC_CR_RST);
+	sonic_write(dev, SONIC_DCR, sonic_dcr
+		    | (dma_bitmode ? SONIC_DCR_DW : 0));
+	/* This *must* be written back to in order to restore the
+           extended programmable output bits */
+	sonic_write(dev, SONIC_DCR2, 0);
+
+	/* Clear *and* disable interrupts to be on the safe side */
+	sonic_write(dev, SONIC_ISR,0x7fff);
+	sonic_write(dev, SONIC_IMR,0);
+
+	/* Now look for the MAC address. */
+	if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0)
+		return -ENODEV;
+
+	printk(KERN_INFO "MAC ");
+	for (i = 0; i < 6; i++) {
+		printk("%2.2x", dev->dev_addr[i]);
+		if (i < 5)
+			printk(":");
+	}
+	printk(" IRQ %d\n", dev->irq);
+
+	/* Shared init code */
+	return macsonic_init(dev);
+}
+
+#ifdef MODULE
+static char namespace[16] = "";
+static struct device dev_macsonic = {
+        NULL,
+        0, 0, 0, 0,
+        0, 0,
+        0, 0, 0, NULL, NULL };
+
+MODULE_PARM(sonic_debug, "i");
+
+EXPORT_NO_SYMBOLS;
+
+int
+init_module(void)
+{
+        dev_macsonic.name = namespace;
+        dev_macsonic.init = macsonic_probe;
+
+        if (register_netdev(&dev_macsonic) != 0) {
+                printk(KERN_WARNING "macsonic.c: No card found\n");
+                return -ENXIO;
+        }
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+        if (dev_macsonic.priv != NULL) {
+                unregister_netdev(&dev_macsonic);
+                kfree(dev_macsonic.priv);
+                dev_macsonic.priv = NULL;
+        }
+}
+#endif /* MODULE */
+
+
+#define vdma_alloc(foo, bar) ((u32)foo)
+#define vdma_free(baz)
+#define sonic_chiptomem(bat) (bat)
+#define PHYSADDR(quux) (quux)
+
+#include "sonic.c"
+
+/*
+ * Local variables:
+ *  compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o macsonic.o macsonic.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  c-indent-level: 8
+ *  tab-width: 8
+ * End:
+ *
+ */
diff -urN linux-2.2.18-pre15/drivers/net/mvme147.c linux/drivers/net/mvme147.c
--- linux-2.2.18-pre15/drivers/net/mvme147.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/mvme147.c	Mon Dec  3 20:34:10 2001
@@ -0,0 +1,209 @@
+/* mvme147.c  : the  Linux/mvme147/lance ethernet driver
+ *
+ * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ * Based on the Sun Lance driver and the NetBSD HP Lance driver
+ * Uses the generic 7990.c LANCE code.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+/* Used for the temporal inet entries and routing */
+#include <linux/socket.h>
+#include <linux/route.h>
+
+#include <linux/dio.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/mvme147hw.h>
+
+/* We have 16834 bytes of RAM for the init block and buffers. This places
+ * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
+ * buffers and 2 Tx buffers.
+ */
+#define LANCE_LOG_TX_BUFFERS 1
+#define LANCE_LOG_RX_BUFFERS 3
+
+#include "7990.h"                                 /* use generic LANCE code */
+
+/* Our private data structure */
+struct m147lance_private {
+	struct lance_private lance;
+	void *base;
+	void *ram;
+};
+
+/* function prototypes... This is easy because all the grot is in the
+ * generic LANCE support. All we have to support is probing for boards,
+ * plus board-specific init, open and close actions.
+ * Oh, and we need to tell the generic code how to read and write LANCE registers...
+ */
+int mvme147lance_probe(struct device *dev);
+static int m147lance_open(struct device *dev);
+static int m147lance_close(struct device *dev);
+static void m147lance_writerap(struct m147lance_private *lp, unsigned short value);
+static void m147lance_writerdp(struct m147lance_private *lp, unsigned short value);
+static unsigned short m147lance_readrdp(struct m147lance_private *lp);
+
+typedef void (*writerap_t)(void *, unsigned short);
+typedef void (*writerdp_t)(void *, unsigned short);
+typedef void (*readrdp_t)(void *);
+
+#ifdef MODULE
+static struct m147lance_private *root_m147lance_dev = NULL;
+#endif
+
+/* Initialise the one and only on-board 7990 */
+__initfunc(int mvme147lance_probe(struct device *dev))
+{
+	static int called = 0;
+	static const char name[] = "MVME147 LANCE";
+	struct m147lance_private *lp;
+	u_long *addr;
+	u_long address;
+
+	if (!MACH_IS_MVME147 || called)
+		return(ENODEV);
+	called++;
+
+	dev->priv = kmalloc(sizeof(struct m147lance_private), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct m147lance_private));
+
+	/* Fill the dev fields */
+	dev->base_addr = (unsigned long)MVME147_LANCE_BASE;
+	dev->open = &m147lance_open;
+	dev->stop = &m147lance_close;
+	dev->hard_start_xmit = &lance_start_xmit;
+	dev->get_stats = &lance_get_stats;
+	dev->set_multicast_list = &lance_set_multicast;
+	dev->dma = 0;
+
+	addr=(u_long *)ETHERNET_ADDRESS;
+	address = *addr;
+	dev->dev_addr[0]=0x08;
+	dev->dev_addr[1]=0x00;
+	dev->dev_addr[2]=0x3e;
+	address=address>>8;
+	dev->dev_addr[5]=address&0xff;
+	address=address>>8;
+	dev->dev_addr[4]=address&0xff;
+	address=address>>8;
+	dev->dev_addr[3]=address&0xff;
+
+	printk("%s: MVME147 at 0x%08lx, irq %d, Hardware Address %02x:%02x:%02x:%02x:%02x:%02x\n",
+		dev->name, dev->base_addr, MVME147_LANCE_IRQ,
+		dev->dev_addr[0],
+		dev->dev_addr[1], dev->dev_addr[2],
+		dev->dev_addr[3], dev->dev_addr[4],
+		dev->dev_addr[5]);
+
+	lp = (struct m147lance_private *)dev->priv;
+	lp->ram = (void *)__get_dma_pages(GFP_ATOMIC, 3);	/* 16K */
+	if (!lp->ram)
+	{
+		printk("%s: No memory for LANCE buffers\n", dev->name);
+		return -ENODEV;
+	}
+
+	lp->lance.name = (char*)name;                   /* discards const, shut up gcc */
+	lp->lance.ll = (struct lance_regs *)(dev->base_addr);
+	lp->lance.init_block = (struct lance_init_block *)(lp->ram); /* CPU addr */
+	lp->lance.lance_init_block = (struct lance_init_block *)(lp->ram);                 /* LANCE addr of same RAM */
+	lp->lance.busmaster_regval = LE_C3_BSWP;        /* we're bigendian */
+	lp->lance.irq = MVME147_LANCE_IRQ;
+	lp->lance.writerap = (writerap_t)m147lance_writerap;
+	lp->lance.writerdp = (writerdp_t)m147lance_writerdp;
+	lp->lance.readrdp = (readrdp_t)m147lance_readrdp;
+	lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
+	lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
+	lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
+	lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
+	ether_setup(dev);
+
+#ifdef MODULE
+	dev->ifindex = dev_new_index();
+	lp->next_module = root_m147lance_dev;
+	root_m147lance_dev = lp;
+#endif /* MODULE */
+
+	return 0;
+}
+
+static void m147lance_writerap(struct m147lance_private *lp, unsigned short value)
+{
+	lp->lance.ll->rap = value;
+}
+
+static void m147lance_writerdp(struct m147lance_private *lp, unsigned short value)
+{
+	lp->lance.ll->rdp = value;
+}
+
+static unsigned short m147lance_readrdp(struct m147lance_private *lp)
+{
+	return lp->lance.ll->rdp;
+}
+
+static int m147lance_open(struct device *dev)
+{
+	int status;
+
+	status = lance_open(dev);                 /* call generic lance open code */
+	if (status)
+		return status;
+	/* enable interrupts at board level. */
+	m147_pcc->lan_cntrl=0;       /* clear the interrupts (if any) */
+	m147_pcc->lan_cntrl=0x08 | 0x04;     /* Enable irq 4 */
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int m147lance_close(struct device *dev)
+{
+	/* disable interrupts at boardlevel */
+	m147_pcc->lan_cntrl=0x0; /* disable interrupts */
+	lance_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	root_lance_dev = NULL;
+	return mvme147lance_probe(NULL);
+}
+
+void cleanup_module(void)
+{
+	/* Walk the chain of devices, unregistering them */
+	struct m147lance_private *lp;
+	while (root_m147lance_dev) {
+		lp = root_m147lance_dev->next_module;
+		unregister_netdev(root_lance_dev->dev);
+		free_pages(lp->ram, 3);
+		kfree(root_lance_dev->dev);
+		root_lance_dev = lp;
+	}
+}
+
+#endif /* MODULE */
diff -urN linux-2.2.18-pre15/drivers/net/xpds/Makefile linux/drivers/net/xpds/Makefile
--- linux-2.2.18-pre15/drivers/net/xpds/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/Makefile	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,23 @@
+# File: drivers/xpds/Makefile
+#
+# Makefile for Xpeed DSL NIC driver
+#
+
+ifeq ($(CONFIG_XPEED),y)
+    O_TARGET := xpds-fr.o
+    O_OBJS = xpds.o xpds-fsm.o xpds-sdsl.o xpds-encap-fr.o
+else
+  ifeq ($(CONFIG_XPEED),m)
+    MOD_LIST_NAME := NET_MODULES
+    M_OBJS := xpds-fr.o
+    O_TARGET := xpds-fr.o
+    O_OBJS = xpds.o xpds-fsm.o xpds-sdsl.o xpds-encap-fr.o
+  endif
+endif
+
+EXTRA_CFLAGS += -I. -DDEBUG=0
+
+include $(TOPDIR)/Rules.make
+
+clean:
+	rm -f core *.o *.a *.s
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-encap-fr.c linux/drivers/net/xpds/xpds-encap-fr.c
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-encap-fr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-encap-fr.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,751 @@
+/*
+ * Copyright 2000 Xpeed, Inc.
+ * fr.c $Revision: 1.23 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ * Some code adapted from Mike McLagan's dlci.c 0.30 from Linux kernels
+ * 2.0-2.2.
+ */
+#define __KERNEL__ 1
+#include <linux/config.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.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/net.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include "xpds.h"
+#include "xpds-encap-fr.h"
+#include "xpds-softnet.h"
+
+#define XPDS_DLCI_LMI_OFF		0
+#define XPDS_DLCI_LMI_LT		1
+#define XPDS_DLCI_LMI_NT		2
+#define XPDS_DLCI_LMI_NT_BIDIRECTIONAL	3
+
+int xpds_dlci_t391 = 10;
+int xpds_dlci_n391 = 6;
+
+#define DEBUG_DLCI	4
+extern int xpds_debug_level;
+
+#if DEBUG
+#define	dprintk	if (xpds_debug_level & DEBUG_DLCI) printk
+#else
+#define	dprintk	if (0) printk
+#endif
+
+#define nrprintk if (net_ratelimit()) printk
+
+typedef struct {
+	u8	dlci_header[2] __attribute__ ((packed));
+	u8	protocol_discriminator __attribute__ ((packed));
+	u8	call_reference __attribute__ ((packed));
+	u8	padding __attribute__ ((packed));
+} dlci_status_head_t __attribute__ ((packed));
+
+typedef struct {
+	u8	message_type __attribute__ ((packed));
+	u8	locking_shift __attribute__ ((packed));
+	u8	report_content_id __attribute__ ((packed));
+	u8	report_content_length __attribute__ ((packed));
+	u8	report_type __attribute__ ((packed));
+	u8	liv_element_id __attribute__ ((packed));
+	u8	liv_length __attribute__ ((packed));
+	u8	liv_send_sequence __attribute__ ((packed));
+	u8	liv_receive_sequence __attribute__ ((packed));
+} dlci_status_tail_t __attribute__ ((packed));
+
+#define MESSAGE_STATUS_ENQUIRY	0x75
+#define MESSAGE_STATUS		0x7d
+
+#define REPORT_FULL_STATUS	0x00
+#define REPORT_LIV		0x01
+#define REPORT_SINGLE_PVC_STATS	0x02
+
+typedef struct {
+	u8	pvc_status_id __attribute__ ((packed));
+	u8	length __attribute__ ((packed));
+	u8	dlci_info[2] __attribute__ ((packed));
+	u8	flags __attribute__ ((packed));
+} dlci_pvc_status_t;
+
+#define PVC_FLAGS__NEW		0x08
+#define PVC_FLAGS__ACTIVE	0x02
+
+typedef struct dlci_timer_data_t {
+	struct net_device	*dev;
+	int		dlci_num;
+} dlci_timer_data_t;
+
+/*
+ * We generate LMI status enquiries in DLCI_LMI_NT mode.
+ */
+static void
+xpds_dlci_lmi_timer (void *p)
+{
+	dlci_timer_data_t	*data;
+	struct net_device		*dev;
+	int			card_num;
+	struct frad_local	*flp;
+	dlci_status_head_t	*status_head;
+	dlci_status_tail_t	*status_tail;
+	struct sk_buff		*skb;
+	short			dlci, lmi_dlci = 0;
+	int			i;
+
+	data = (dlci_timer_data_t *) p;
+
+	dev = data->dev;
+	card_num = dev - xpds_devs;
+	flp = &(xpds_data[card_num].frad_data);
+
+	dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name);
+
+	if (xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT &&
+		xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT_BIDIRECTIONAL){
+		return;
+	}
+
+	if (flp->no_initiate_lmi) return;
+
+	dlci = flp->dlci[data->dlci_num];
+	dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer(), dev = %p, dlci = %d\n", xpds_devs[card_num].name, dev, dlci);
+
+	if (xpds_if_busy(dev)) {
+
+		skb = alloc_skb (sizeof (dlci_status_head_t) +
+			sizeof (dlci_status_tail_t), GFP_ATOMIC);
+		if (skb == NULL) {
+			printk (KERN_ERR "%s: unable to allocate skb in xlds_dlci_lmi_timer()\n", dev->name);
+			return;
+		}
+		skb->dev = dev;
+		skb->len = sizeof (dlci_status_head_t) +
+			sizeof (dlci_status_tail_t);
+		status_head = (dlci_status_head_t *) skb->data;
+		status_tail = (dlci_status_tail_t *) (skb->data +
+			sizeof (dlci_status_head_t));
+		status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) |
+			xpds_data[card_num].dlci_cr;
+		status_head->dlci_header[1] = (lmi_dlci << 4) | 1;
+		status_head->protocol_discriminator = 0x03;
+		status_head->call_reference = 0x08;
+		status_head->padding = 0;
+
+		status_tail->message_type = MESSAGE_STATUS_ENQUIRY;
+		status_tail->locking_shift = 0x95;
+		status_tail->report_content_id = 0x01;
+		status_tail->report_content_length = 0x01;
+		status_tail->liv_element_id = 0x03 /* 0x19 */;
+		status_tail->liv_length = 0x02;
+
+		status_tail->liv_send_sequence = (flp->liv_send_sequence) ++;
+		status_tail->liv_receive_sequence = flp->liv_receive_sequence;
+		if (flp->pvc_active[data->dlci_num]) {
+			if (flp->remote_liv_receive_sequence >=
+				flp->new_liv_send_sequence) {
+				flp->pvc_new[data->dlci_num] = 0;
+			} else {
+				flp->pvc_new[data->dlci_num] = 1;
+			}
+		} else {
+			flp->new_liv_send_sequence =
+				status_tail->liv_send_sequence;
+		}
+		dprintk (KERN_DEBUG "%s: dlci = %d ", xpds_devs[card_num].name, dlci);
+		dprintk (KERN_DEBUG "pvc = %d, new = %d\n",
+			flp->pvc_active[data->dlci_num], flp->pvc_new[data->dlci_num]);
+		dprintk (KERN_DEBUG "%s: liv_send_sequence = %d, liv_receive_sequence = %d\n", xpds_devs[card_num].name, status_tail->liv_send_sequence, status_tail->liv_receive_sequence);
+		flp->message_number ++;
+		if (flp->message_number >= xpds_dlci_n391) {
+			flp->message_number = 0;
+		}
+		status_tail->report_type =
+			(flp->message_number == 0) ?
+				REPORT_FULL_STATUS : REPORT_LIV;
+		dprintk (KERN_DEBUG "message_number = %d, sending %02x\n",
+			flp->message_number, status_tail->report_type);
+		dprintk (KERN_DEBUG "%s: sending LMI packet in xpds_dlci_lmi_timer()\n", xpds_devs[card_num].name);
+		xpds_tx (skb->data, skb->len, dev);
+
+		dev_kfree_skb (skb);
+	}
+
+	i = data->dlci_num;
+
+	init_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+	xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer;
+	xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ;
+	xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]);
+	add_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+
+	dprintk(KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name);
+}
+
+void
+xpds_dlci_install_lmi_timer (int i, struct net_device *dev)
+{
+	int	card_num;
+
+	card_num = dev - xpds_devs;
+
+	dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer (%d, %p)\n", xpds_devs[card_num].name, i, dev);
+	if (i < 0 || i >= CONFIG_DLCI_COUNT) {
+		printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i);
+		return;
+	}
+	init_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+	xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer;
+	xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ;
+	xpds_data[card_num].dlci_lmi_timer_data[i].dev = dev;
+	xpds_data[card_num].dlci_lmi_timer_data[i].dlci_num = i;
+	xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]);
+	add_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+	dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer() done\n",
+		dev->name);
+}
+
+void
+xpds_dlci_remove_lmi_timer (int i, struct net_device *dev)
+{
+	int	card_num;
+
+	card_num = dev - xpds_devs;
+
+	dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer (%d, %p)\n",
+		dev->name, i, dev);
+	if (i < 0 || i >= CONFIG_DLCI_COUNT) {
+		printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i);
+		return;
+	}
+	del_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+	dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer() done\n",
+		dev->name);
+}
+
+
+static void
+xpds_dlci_handle_status (struct sk_buff *skb, struct net_device *dev)
+{
+	struct frad_local	*flp;
+	dlci_status_head_t	*dlci_status_head, *reply_status_head;
+	dlci_status_tail_t	*dlci_status_tail, *reply_status_tail;
+	dlci_pvc_status_t	*dlci_pvc_status, *reply_pvc_status;
+	struct sk_buff		*reply_skb;
+	short			dlci, lmi_dlci = 0;
+	int			i;
+	int			old_pvc_active;
+	int			has_pvc_status;
+	int			has_padding;
+	int			card_num;
+
+	card_num = dev - xpds_devs;
+
+	if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_OFF) return;
+
+	flp = &(xpds_data[card_num].frad_data);
+	skb->dev = dev;
+
+	dlci_status_head = (dlci_status_head_t *)skb->data;
+	if (dlci_status_head->padding != 0) {
+		/*
+		 * no padding
+		 */
+		has_padding = 0;
+	} else {
+		/*
+		 * one byte of zero padding between call reference
+		 * and message type
+		 */
+		has_padding = 1;
+	}
+	dlci_status_tail = (dlci_status_tail_t *)
+		(skb->data + sizeof (*dlci_status_head) - 1 + has_padding);
+	if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding) {
+		dprintk (KERN_ERR "%s: LMI packet of length %lu is too short\n", xpds_devs[card_num].name, (unsigned long) (skb->len));
+	}
+	dlci_pvc_status = (dlci_pvc_status_t *)
+		(skb->data + sizeof (*dlci_status_head) +
+		sizeof (*dlci_status_tail) - 1 + has_padding);
+
+	dprintk (KERN_DEBUG "%s: LMI packet received has protocol discriminator = 0x%02x\n", xpds_devs[card_num].name, dlci_status_head->protocol_discriminator);
+	dprintk (KERN_DEBUG "%s: LMI packet received has call reference = 0x%02x\n",
+		dev->name, dlci_status_head->call_reference);
+	if (dlci_status_head->protocol_discriminator != FRAD_I_UI ||
+		dlci_status_head->call_reference != FRAD_P_Q933) {
+		printk (KERN_NOTICE "%s: dlci_handle_status called with 0x%02x 0x%02x\n",
+			dev->name, dlci_status_head->protocol_discriminator,
+			dlci_status_head->call_reference);
+		return;
+	}
+
+	dprintk (KERN_DEBUG "LMI packet received has message type = 0x%02x\n",
+		dlci_status_tail->message_type);
+	if (dlci_status_tail->message_type != MESSAGE_STATUS_ENQUIRY &&
+		dlci_status_tail->message_type != MESSAGE_STATUS) {
+		printk (KERN_NOTICE "unknown message type 0x%02x\n",
+			dlci_status_tail->message_type);
+		return;
+	}
+
+	dprintk (KERN_DEBUG "LMI packet received has report type = 0x%02x\n",
+		dlci_status_tail->report_type);
+	if (dlci_status_tail->report_type != REPORT_FULL_STATUS &&
+		dlci_status_tail->report_type != REPORT_LIV) {
+		dprintk (KERN_DEBUG "report type is not full status or LIV\n");
+		return;
+	}
+
+	dprintk (KERN_DEBUG "LMI packet received has receive sequence = %d\n",
+		dlci_status_tail->liv_receive_sequence);
+	dprintk (KERN_DEBUG "LMI packet received has send sequence = %d\n",
+		dlci_status_tail->liv_send_sequence);
+
+	reply_skb = alloc_skb (sizeof (dlci_status_head_t) +
+		sizeof (dlci_status_tail_t) + sizeof (dlci_pvc_status_t),
+		GFP_ATOMIC);
+	if (reply_skb == NULL) {
+		printk (KERN_ERR "%s: unable to allocate reply_skb in xpds_dlci_handle_status().\n", dev->name);
+		return;
+	}
+	reply_skb->len = sizeof (dlci_status_head_t) +
+		sizeof (dlci_status_tail_t) /* + sizeof (dlci_pvc_status_t) */;
+	reply_status_head = (dlci_status_head_t *) reply_skb->data;
+	reply_status_tail = (dlci_status_tail_t *) (reply_skb->data +
+		sizeof (dlci_status_head_t));
+	reply_pvc_status = (dlci_pvc_status_t *) (reply_skb->data +
+		sizeof (dlci_status_head_t) + sizeof (dlci_status_tail_t) );
+
+	if (dlci_status_tail->liv_receive_sequence > flp->liv_send_sequence){
+		dprintk (KERN_DEBUG "received receive sequence number %d > %d\n",
+			dlci_status_tail->liv_receive_sequence,
+			flp->liv_send_sequence);
+	}
+	flp->remote_liv_receive_sequence =
+		dlci_status_tail->liv_receive_sequence;
+	flp->liv_send_sequence = dlci_status_tail->liv_receive_sequence + 1;
+
+	flp->remote_liv_send_sequence =
+		dlci_status_tail->liv_send_sequence;
+	flp->liv_receive_sequence = dlci_status_tail->liv_send_sequence;
+
+	if (dlci_status_tail->report_type == REPORT_FULL_STATUS) {
+		if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding + sizeof (*dlci_pvc_status)) {
+			if (dlci_status_tail->message_type == MESSAGE_STATUS) {
+				printk (KERN_ERR "%s: LMI packet length %lu is too short for report type REPORT_FULL_STATUS\n", xpds_devs[card_num].name, (unsigned long) (skb->len));
+			}
+			dlci = -1;
+			has_pvc_status = 0;
+		} else {
+			dlci = ((dlci_pvc_status->dlci_info[0] & 0x7f) << 4) |
+				((dlci_pvc_status->dlci_info[1] & 0x7f) >> 3);
+			has_pvc_status = 1;
+		}
+	} else {
+		dprintk (KERN_DEBUG "%s: dlci_status_tail->report_type = 0x%02x (not REPORT_FULL_STATUS)\n", xpds_devs[card_num].name, dlci_status_tail->report_type);
+		dlci = -1;
+		has_pvc_status = 0;
+	}
+	dprintk (KERN_DEBUG "%s: LMI packet received has DLCI = %d, has_pvc_status = %d\n", xpds_devs[card_num].name, dlci, has_pvc_status);
+
+	if (has_pvc_status) {
+		for (i = 0; i < CONFIG_DLCI_MAX; i ++) {
+			if (dlci == flp->dlci[i]) break;
+		}
+		if (i >= CONFIG_DLCI_MAX) {
+			int	j;
+
+			printk (KERN_ERR "%s: invalid DLCI %d\n",
+				dev->name, dlci);
+
+			printk (KERN_ERR "%s: flp->dlci[] = {", xpds_devs[card_num].name);
+			for (j = 0; j < CONFIG_DLCI_MAX; j ++) {
+				if (j != 0) printk (", ");
+				printk ("%d", flp->dlci[j]);
+			}
+			printk ("}\n");
+			dev_kfree_skb (reply_skb);
+			return;
+		}
+	} else {
+		i = 0;
+	}
+
+	reply_status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) |
+		xpds_data[card_num].dlci_cr;
+	reply_status_head->dlci_header[1] = (lmi_dlci << 4) | 1;
+	reply_status_head->protocol_discriminator = 0x03;
+	reply_status_head->call_reference = 0x08;
+	reply_status_head->padding = 0;
+
+	reply_status_tail->message_type = MESSAGE_STATUS;
+	reply_status_tail->locking_shift = 0x95;
+	reply_status_tail->report_content_id = 0x01;
+	reply_status_tail->report_content_length = 0x01;
+	flp->message_number ++;
+	if (flp->message_number >= xpds_dlci_n391) {
+		flp->message_number = 0;
+	}
+	reply_status_tail->report_type =
+		(flp->message_number == 0) ?
+			REPORT_FULL_STATUS : REPORT_LIV;
+	dprintk (KERN_DEBUG "%s: message_number = %d, sending 0x%02x\n",
+		dev->name, flp->message_number, reply_status_tail->report_type);
+	reply_status_tail->liv_element_id = 0x03 /* 0x19 */;
+	reply_status_tail->liv_length = 0x02;
+	reply_status_tail->liv_send_sequence = (flp->liv_send_sequence) ++;
+	reply_status_tail->liv_receive_sequence = flp->liv_receive_sequence;
+
+	if (has_pvc_status) {
+		reply_pvc_status->pvc_status_id = 0x7;
+		reply_pvc_status->length = 3;
+		reply_pvc_status->dlci_info[0] = (dlci >> 6) & 0x3f;
+		reply_pvc_status->dlci_info[1] = ((dlci << 3) & 0x78) | 0x80;
+
+		dprintk (KERN_DEBUG "dlci_pvc_status->pvc_status_id = 0x%02x\n",
+			dlci_pvc_status->pvc_status_id);
+		dprintk (KERN_DEBUG "dlci_pvc_status->length = 0x%02x\n",
+			dlci_pvc_status->length);
+		dprintk (KERN_DEBUG "dlci_pvc_status->dlci_info = 0x%02x 0x%02x\n", dlci_pvc_status->dlci_info[0], dlci_pvc_status->dlci_info[1]);
+		dprintk (KERN_DEBUG "dlci_pvc_status->flags = 0x%02x\n",
+			dlci_pvc_status->flags);
+		old_pvc_active = flp->pvc_active[i];
+		flp->pvc_active[i] = (dlci_pvc_status->flags & PVC_FLAGS__ACTIVE) != 0;
+		if (! old_pvc_active && flp->pvc_active[i]) {
+			printk (KERN_NOTICE "%s: DLCI %d PVC became active\n", xpds_devs[card_num].name, dlci);
+		}
+		if (old_pvc_active && ! flp->pvc_active[i]) {
+			printk (KERN_NOTICE "%s: DLCI %d PVC became inactive\n", xpds_devs[card_num].name, dlci);
+		}
+		if (flp->pvc_active[i]) {
+			if (dlci_status_tail->liv_receive_sequence >=
+				flp->new_liv_send_sequence) {
+				flp->pvc_new[i] = 0;
+			} else {
+				flp->pvc_new[i] = 1;
+			}
+		} else {
+			flp->new_liv_send_sequence =
+				reply_status_tail->liv_send_sequence;
+		}
+		dprintk (KERN_DEBUG "dlci = %d\n", dlci);
+		dprintk (KERN_DEBUG "pvc = %d, new = %d\n",
+			flp->pvc_active[i], flp->pvc_new[i]);
+		dprintk (KERN_DEBUG "liv_send_sequence = %d, liv_receive_sequence = %d\n", reply_status_tail->liv_send_sequence, reply_status_tail->liv_receive_sequence);
+		reply_pvc_status->flags = 0x80 | (flp->pvc_new[i] << 3) |
+			(flp->pvc_active[i] << 1);
+	}
+
+	/* ... */
+
+	if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_LT ||
+		xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_NT_BIDIRECTIONAL){
+		dprintk (KERN_DEBUG "sending LMI packet in xpds_dlci_handle_status()\n");
+		xpds_tx (reply_skb->data, reply_skb->len, dev);
+		dev_kfree_skb(reply_skb);
+	}
+
+	/* This is freed by the caller. */
+	/* dev_kfree_skb(skb); */
+}
+
+void
+xpds_dlci_receive(struct sk_buff *skb, struct net_device *dev)
+{
+	int card_num;
+	struct frhdr *hdr;
+	struct frad_local *flp;
+	int process, header, actual_header_size;
+
+	dprintk(KERN_DEBUG "xpds_dlci_receive (%p, %p)\n", skb, dev);
+	card_num = dev - xpds_devs;
+	flp = &(xpds_data[card_num].frad_data);
+	hdr = (struct frhdr *) (skb->data);
+	process = 0;
+	header = sizeof(hdr->addr_control);
+	skb->dev = dev;
+
+	actual_header_size = skb->tail - skb->data;
+	if (actual_header_size < header + 2) {
+		nrprintk (KERN_NOTICE "%s: header is too short (%d bytes)\n",
+			dev->name, skb->tail - skb->data);
+		dev_kfree_skb(skb);
+		xpds_data[card_num].stats.rx_errors++;
+		return;
+	}
+
+	if (hdr->control != FRAD_I_UI) {
+		nrprintk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n",
+		       dev->name, hdr->control);
+		xpds_data[card_num].stats.rx_errors++;
+	} else {
+		switch (hdr->IP_NLPID) {
+		case FRAD_P_PADDING:
+			if (hdr->NLPID != FRAD_P_SNAP) {
+				nrprintk(KERN_NOTICE
+				       "%s: Unsupported NLPID 0x%02X.\n",
+				       dev->name, hdr->NLPID);
+				xpds_data[card_num].stats.rx_errors++;
+				break;
+			}
+
+			if (actual_header_size < sizeof (struct frhdr)) {
+				nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_PADDING)\n", dev->name, skb->tail - skb->data);
+				dev_kfree_skb(skb);
+				return;
+			}
+
+			if (hdr->OUI[0] == FRAD_OUI_BRIDGED_0 &&
+			    hdr->OUI[1] == FRAD_OUI_BRIDGED_1 &&
+			    hdr->OUI[2] == FRAD_OUI_BRIDGED_2) {
+				/* bridged ethernet */
+
+				struct ethhdr *ehdr;
+
+				header = sizeof(struct frhdr);
+				ehdr =
+				    (struct ethhdr *) (skb->data + header);
+				skb->protocol = ntohs(ehdr->h_proto);
+				/* skb->mac.raw = (char *)&(ehdr->h_dest); */
+				dprintk(KERN_DEBUG
+					"hdr->NLPID = FRAD_P_SNAP (%02X), protocol = 0x%04x\n",
+					FRAD_P_SNAP, skb->protocol);
+				process = 1;
+				break;
+			}
+
+			if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0) {
+				nrprintk(KERN_NOTICE
+				       "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n",
+				       dev->name, hdr->OUI[0], hdr->OUI[1],
+				       hdr->OUI[2]);
+				xpds_data[card_num].stats.rx_errors++;
+				break;
+			}
+
+			/* at this point, it's an EtherType frame */
+			header = sizeof(struct frhdr);
+			/* Already in network order ! */
+			skb->protocol = hdr->PID;
+			process = 1;
+			break;
+
+		case FRAD_P_IP:
+			dprintk(KERN_DEBUG
+				"hdr->IP_NLPID = FRAD_P_IP (%02X)\n",
+				FRAD_P_IP);
+			header =
+			    sizeof(hdr->addr_control) +
+			    sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
+			if (actual_header_size < header) {
+				nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_IP)\n", dev->name, skb->tail - skb->data);
+				xpds_data[card_num].stats.rx_errors++;
+			} else {
+				skb->protocol = htons(ETH_P_IP);
+				process = 1;
+			}
+			break;
+
+		case FRAD_P_Q933:
+			/* status / status enquiry message */
+			dprintk(KERN_DEBUG
+				"hdr->IP_NLPID = FRAD_P_Q933 (%02X)\n",
+				FRAD_P_Q933);
+			xpds_dlci_handle_status(skb, dev);
+			break;
+		case FRAD_P_SNAP:
+			dprintk(KERN_DEBUG
+				"hdr->IP_NLPID = FRAD_P_SNAP (%02X)\n",
+				FRAD_P_SNAP);
+			skb->protocol = htons(ETH_P_ARP);
+			process = 1;
+			break;
+		case FRAD_P_CLNP:
+			nrprintk(KERN_NOTICE
+			       "%s: Unsupported NLPID 0x%02X.\n",
+			       dev->name, hdr->pad);
+			xpds_data[card_num].stats.rx_errors++;
+			break;
+
+		default:
+			nrprintk(KERN_NOTICE
+			       "%s: Invalid pad byte 0x%02X.\n", dev->name,
+			       hdr->pad);
+			xpds_data[card_num].stats.rx_errors++;
+			break;
+		}
+	}
+
+	if (process) {
+		dprintk(KERN_DEBUG "%s: header size = %d\n", dev->name,
+			header);
+		if (xpds_data[card_num].bridged_ethernet) {
+			skb_pull(skb, header);
+		} else {
+			struct ethhdr *ehdr;
+			skb_pull(skb, header);
+			skb_push(skb, sizeof(struct ethhdr));
+			ehdr = (struct ethhdr *) skb->data;
+			memcpy(ehdr->h_dest,
+			       xpds_data[card_num].serial_data.mac_address,
+			       sizeof(ehdr->h_dest));
+			/* +++ */
+			memset(ehdr->h_source, 0xff,
+			       sizeof(ehdr->h_source));
+			/* +++ */
+			ehdr->h_proto = skb->protocol;
+		}
+		skb->ip_summed = CHECKSUM_NONE;
+#if DEBUG
+		{
+			long	i, skblen;
+
+			dprintk(KERN_DEBUG
+				"%s: packet of length %ld to be given to netif_rx():", dev->name, (long) (skb->len));
+			skblen = skb->len > 256 ? 256 : skb->len;
+			for (i = 0; i < skblen; i++) {
+				dprintk(" %02x", skb->data[i]);
+			}
+			if (skb->len > skblen) dprintk (" ...");
+			dprintk("\n");
+			dprintk(KERN_DEBUG
+				"%s: skb->head = %p, skb->data = %p, skb->tail = %p, skb->end = %p\n",
+				dev->name, skb->head, skb->data, skb->tail,
+				skb->end);
+		}
+#endif
+		skb->protocol = eth_type_trans(skb, dev);
+		dprintk(KERN_DEBUG "calling netif_rx (%p)\n", skb);
+		netif_rx(skb);
+	} else {
+		dev_kfree_skb(skb);
+	}
+	dprintk(KERN_DEBUG "xpds_dlci_receive done\n");
+}
+
+static unsigned int
+xpds_dlci_add_fr_header(struct sk_buff *skb, struct net_device *dev,
+			unsigned short type, u8 * hdr)
+{
+	unsigned hlen;
+	short dlci;
+	struct frad_local *flp;
+	int card_num;
+	struct frhdr *frhdr;
+
+	frhdr = (struct frhdr *) hdr;
+	card_num = dev - xpds_devs;
+	dprintk(KERN_DEBUG "%s: adding frame relay header\n", dev->name);
+	flp = &(xpds_data[card_num].frad_data);
+	dlci = xpds_data[card_num].dlci;
+	hdr[0] = ((dlci >> 4) << 2) | xpds_data[card_num].dlci_cr;
+	hdr[1] = (dlci << 4) | 1;
+
+	frhdr->control = FRAD_I_UI;
+	dprintk(KERN_DEBUG "%s: type = 0x%04x\n", dev->name, type);
+	switch (type) {
+	case ETH_P_IP:
+	case ETH_P_ARP:
+		if (xpds_data[card_num].bridged_ethernet) {
+			frhdr->pad = FRAD_P_PADDING;
+			frhdr->NLPID = FRAD_P_SNAP;
+			frhdr->OUI[0] = FRAD_OUI_BRIDGED_0;
+			frhdr->OUI[1] = FRAD_OUI_BRIDGED_1;
+			frhdr->OUI[2] = FRAD_OUI_BRIDGED_2;
+			frhdr->PID = htons(FRAD_PID);
+			hlen = sizeof(*frhdr);
+		} else {
+			frhdr->IP_NLPID = FRAD_P_IP;
+			hlen =
+			    sizeof(frhdr->addr_control) + 
+			    sizeof(frhdr->control) +
+			    sizeof(frhdr->IP_NLPID);
+		}
+		break;
+
+		/* feel free to add other types, if necessary */
+
+	default:
+		frhdr->pad = FRAD_P_PADDING;
+		frhdr->NLPID = FRAD_P_SNAP;
+		memset(frhdr->OUI, 0, sizeof(frhdr->OUI));
+		frhdr->PID = htons(type);
+		hlen = sizeof(*frhdr);
+		break;
+	}
+
+	dprintk(KERN_DEBUG "type = 0x%02x, bridged = %d, hlen = %d\n",
+		type, xpds_data[card_num].bridged_ethernet, hlen);
+
+#if DEBUG
+	{
+		int i;
+
+		dprintk(KERN_DEBUG "dlci_header added:");
+		for (i = 0; i < hlen; i++) {
+			dprintk(" %02x", ((u8 *) hdr)[i]);
+		}
+		dprintk("\n");
+	}
+#endif
+	return hlen;
+}
+
+int
+xpds_dlci_transmit(struct sk_buff *skb, struct net_device *dev)
+{
+	u8 *hdr, *buffer;
+	int card_num, rc;
+	unsigned int ptr, len;
+
+	dprintk(KERN_DEBUG "xpds_dlci_transmit (%p, %p)\n", skb, dev);
+	card_num = dev - xpds_devs;
+
+	buffer = kmalloc (skb->len + sizeof(struct frhdr), GFP_ATOMIC);
+	if (buffer == NULL) {
+		printk (KERN_ERR "%s: failed to allocate buffer in xpds_dlci_transmit()\n", xpds_devs[card_num].name);
+		return -ENOMEM;
+	}
+	hdr = buffer;
+	ptr = xpds_dlci_add_fr_header(skb, dev, ntohs(skb->protocol), hdr);
+
+	if (xpds_data[card_num].bridged_ethernet) {
+		memcpy(buffer + ptr, skb->data,
+		       sizeof(struct ethhdr));
+		ptr += sizeof(struct ethhdr);
+	}
+	memcpy(buffer + ptr,
+	       skb->data + sizeof(struct ethhdr),
+	       skb->len - sizeof(struct ethhdr));
+	len = ptr + skb->len - sizeof(struct ethhdr);
+
+	rc = xpds_tx(buffer, len, dev);
+	kfree (buffer);
+
+	if (rc) {
+		xpds_data[card_num].stats.tx_errors++;
+	} else {
+		xpds_data[card_num].stats.tx_packets++;
+		dev_kfree_skb(skb);
+	}
+
+	dprintk(KERN_DEBUG "xpds_dlci_transmit done\n");
+	return rc;
+}
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-encap-fr.h linux/drivers/net/xpds/xpds-encap-fr.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-encap-fr.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-encap-fr.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000, Xpeed, Inc.
+ * fr.h $Revision: 1.10 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ * Some code adapted from Mike McLagen's /usr/include/linux/if_frad.h in
+ * Linux kernels 2.0-2.2.
+ */
+
+#ifndef XPDS_ENCAP_FR_H
+#define XPDS_ENCAP_FR_H
+
+#include <linux/config.h>
+#include <linux/if.h>
+#include <asm/types.h>
+#include "xpds-softnet.h"
+
+#ifndef CONFIG_DLCI_MAX
+#define CONFIG_DLCI_MODULE	1
+#define CONFIG_DLCI_MAX	8
+#define CONFIG_DLCI_COUNT	24
+#endif
+
+#define FRAD_STATION_CPE	0x0000
+#define FRAD_STATION_NODE	0x0001
+
+#define FRAD_TX_IGNORE_CIR	0x0001
+#define FRAD_RX_ACCOUNT_CIR	0x0002
+#define FRAD_DROP_ABORTED	0x0004
+#define FRAD_BUFFERIF		0x0008
+#define FRAD_STATS		0x0010
+#define FRAD_MCI		0x0100
+#define FRAD_AUTODLCI		0x8000
+#define FRAD_VALID_FLAGS	0x811F
+
+#define FRAD_CLOCK_INT		0x0001
+#define FRAD_CLOCK_EXT		0x0000
+
+/* these are the fields of an RFC 1490 header */
+struct frhdr {
+	u8 addr_control[2] __attribute__ ((packed));
+	u8 control __attribute__ ((packed));
+
+	/* for IP packets, this can be the NLPID */
+	u8 pad __attribute__ ((packed));
+
+	u8 NLPID __attribute__ ((packed));
+	u8 OUI[3] __attribute__ ((packed));
+	u16 PID __attribute__ ((packed));
+
+#define IP_NLPID pad
+} __attribute__ ((packed));
+
+/* see RFC 1490 for the definition of the following */
+#define FRAD_I_UI		0x03
+
+#define FRAD_P_PADDING		0x00
+#define FRAD_P_Q933		0x08
+#define FRAD_P_SNAP		0x80
+#define FRAD_P_CLNP		0x81
+#define FRAD_P_IP		0xCC
+
+#define FRAD_OUI_BRIDGED_0	0x00
+#define FRAD_OUI_BRIDGED_1	0x80
+#define FRAD_OUI_BRIDGED_2	0xc2
+
+#define FRAD_PID		0x0007
+
+struct frad_local {
+	short dlci[CONFIG_DLCI_MAX];
+
+	/* fields for LMI messages */
+	int liv_send_sequence;
+	int liv_receive_sequence;
+	int remote_liv_send_sequence;
+	int remote_liv_receive_sequence;
+	int new_liv_send_sequence;
+	int pvc_active[CONFIG_DLCI_MAX];
+	int pvc_new[CONFIG_DLCI_MAX];
+	int no_initiate_lmi;
+	int message_number;
+
+};
+
+extern void xpds_dlci_receive(struct sk_buff *skb, struct net_device *dev);
+extern int xpds_dlci_transmit(struct sk_buff *skb, struct net_device *dev);
+
+#define XPDS_DEFAULT_DLCI	16
+#define XPDS_DEFAULT_DLCI_CR	0 /* 0 or 2 are the possible values */
+
+#define XPDS_DLCI_LMI_LT_OR_NT		(-1)
+#define XPDS_DLCI_LMI_NONE		0
+#define XPDS_DLCI_LMI_LT		1
+#define XPDS_DLCI_LMI_NT		2
+#define XPDS_DLCI_LMI_NT_BIDIRECTIONAL	3
+
+typedef struct dlci_lmi_timer_data_t {
+        struct net_device   *dev;
+	int             dlci_num;
+} dlci_lmi_timer_data_t;
+
+void xpds_dlci_install_lmi_timer (int i, struct net_device *dev);
+void xpds_dlci_remove_lmi_timer (int i, struct net_device *dev);
+
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-fsm.c linux/drivers/net/xpds/xpds-fsm.c
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-fsm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-fsm.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,790 @@
+/*
+ * Copyright 1998, 1999, 2000 Xpeed, Inc.
+ * xpds-fsm.c, $Revision: 1.2 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifdef DEBUG
+#define dprintk         if (xpds_debug_level & DEBUG_FSM) printk
+#else
+#define dprintk         if (0) printk
+#endif
+ 
+#ifndef __KERNEL__
+#define __KERNEL__ 1
+#endif
+#ifndef MODULE
+#define MODULE 1
+#endif
+
+#define __NO_VERSION__ 1
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h>
+
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#if ! defined (CONFIG_PCI)
+#error "CONFIG_PCI is not defined"
+#endif
+
+#include "xpds-reg.h"
+#include "xpds-softnet.h"
+#include "xpds.h"
+
+/*
+ * Time periods in milliseconds.
+ */
+#define JIFFIES_COUNT(t)	((t)*HZ/1000?(t)*HZ/1000:1)
+
+#define T1 	JIFFIES_COUNT(15000)
+#define T2 	JIFFIES_COUNT(3)
+#define T3 	JIFFIES_COUNT(40)
+#define T4 	JIFFIES_COUNT(6000)
+#define T5 	JIFFIES_COUNT(1000)
+#define T6 	JIFFIES_COUNT(6000)
+#define T7 	JIFFIES_COUNT(40)
+#define T8 	JIFFIES_COUNT(24)
+#define T9 	JIFFIES_COUNT(40)
+#define T10 	JIFFIES_COUNT(40)
+#define T11 	JIFFIES_COUNT(9)
+#define T12	JIFFIES_COUNT(5500)
+#define T13	JIFFIES_COUNT(15000)
+#define T14	JIFFIES_COUNT(1/2)
+
+static unsigned long	timer_limits[15] = {
+	0, T1, T2, T3, T4, T5, T6, T7,
+	T8, T9, T10, T11, T12, T13, T14
+};
+
+static unsigned long	timers[15];
+
+#define MODE_LT	0
+#define MODE_NT	1
+
+/*
+ * States
+ */
+#define	FSM_LT_RECEIVE_RESET	0
+#define	FSM_LT_AWAKE_ERROR	1
+#define	FSM_LT_DEACTIVATED	2
+#define	FSM_LT_RESET_FOR_LOOP	3
+#define	FSM_LT_ALERTING		4
+#define	FSM_LT_WAIT_FOR_TN	5
+#define	FSM_LT_AWAKE		6
+#define	FSM_LT_EC_TRAINING	7
+#define	FSM_LT_EC_CONVERGED	8
+#define	FSM_LT_EQ_TRAINING	9
+#define	FSM_LT_LINE_ACTIVE	10
+#define	FSM_LT_PENDING_TRANSPARENT	11
+#define FSM_LT_TRANSPARENT	12
+#define FSM_LT_ST_DEACTIVATED	13
+#define	FSM_LT_PENDING_DEACTIVATION	14
+#define	FSM_LT_LOSS_OF_SYNC	15
+#define	FSM_LT_LOSS_OF_SIGNAL	16
+#define	FSM_LT_TEAR_DOWN_ERROR	17
+#define FSM_LT_TEST		18
+#define FSM_LT_TEAR_DOWN	19
+
+#define	FSM_NT_RECEIVE_RESET	0
+#define	FSM_NT_PENDING_TIMING	1
+#define	FSM_NT_DEACTIVATED	2
+#define	FSM_NT_IOM_AWAKED	3
+#define	FSM_NT_ALERTING		4
+#define	FSM_NT_EC_TRAINING	5
+#define	FSM_NT_EQ_TRAINING	6
+#define	FSM_NT_WAIT_FOR_SF	7
+#define	FSM_NT_SYNCHRONIZED_1	8
+#define	FSM_NT_SYNCHRONIZED_2	9
+#define	FSM_NT_WAIT_FOR_ACT	10
+#define	FSM_NT_TRANSPARENT	11
+#define FSM_NT_ALERTING_1	12
+#define FSM_NT_EC_TRAINING_1	13
+#define	FSM_NT_PEND_DEACT_ST	14
+#define	FSM_NT_ERROR_ST		15
+#define	FSM_NT_PEND_RECEIVE_RES	16
+#define	FSM_NT_PEND_DEACT_U	17
+#define FSM_NT_TEST		18
+#define FSM_NT_EC_TRAINING_AL	19
+#define FSM_NT_WAIT_FOR_SF_AL	20
+#define FSM_NT_ANALOG_LOOP_BACK	21
+
+static int	lt_state = FSM_LT_TEST;
+static int	nt_state = FSM_NT_RECEIVE_RESET;
+
+/*
+ * Commands (written to TXCI in MCR)
+ */
+#define CMD_NULL		(~0)
+
+#define	CMD_LT_DR		0x00
+#define	CMD_LT_RES		0x01
+#define CMD_LT_LTD		0x03
+#define CMD_LT_RES1		0x04
+#define CMD_LT_SSP		0x05
+#define CMD_LT_DT		0x06
+#define CMD_LT_UAR		0x07
+#define CMD_LT_AR		0x08
+#define CMD_LT_ARL		0x0a
+#define CMD_LT_AR0		0x0d
+#define CMD_LT_DC		0x0f
+
+#define	CMD_NT_TIMING		0x00
+#define	CMD_NT_RESET		0x01
+#define CMD_NT_DU		0x03
+#define CMD_NT_EI1		0x04
+#define CMD_NT_SSP		0x05
+#define CMD_NT_DT		0x06
+#define CMD_NT_AR		0x08
+#define CMD_NT_ARL		0x0a
+#define CMD_NT_AI		0x0c
+#define CMD_NT_DI		0x0f
+
+#define TXCI_VAL(cmd,channels)	(((cmd) & XPDS_MCR_TXCI__MASK) | \
+					(channels) | \
+					XPDS_MCR_TXCI__PMD_ENABLE | \
+					XPDS_MCR_TXCI__PMD_RESQ)
+/*
+ * Indications (read from RXCI in MCR)
+ */
+#define IND_LT_NULL		(~0)
+#define IND_LT_DEAC		0x01
+#define IND_LT_FJ		0x02
+#define IND_LT_HI		0x03
+#define IND_LT_RSY		0x04
+#define IND_LT_EI2		0x05
+#define IND_LT_INT		0x06
+#define IND_LT_UAI		0x07
+#define IND_LT_AR		0x08
+#define IND_LT_ARM		0x09
+#define IND_LT_EI3		0x0b
+#define IND_LT_AI		0x0c
+#define IND_LT_LSL		0x0d
+#define IND_LT_DI		0x0f
+
+#define IND_NT_NULL		(~0)
+#define IND_NT_DR		0x00
+#define IND_NT_FJ		0x02
+#define IND_NT_EI1		0x04
+#define IND_NT_INT		0x06
+#define IND_NT_PU		0x07
+#define IND_NT_AR		0x08
+#define IND_NT_ARL		0x0a
+#define IND_NT_AI		0x0c
+#define IND_NT_AIL		0x0e
+#define IND_NT_DC		0x0f
+
+typedef struct {
+	int		state;
+	u32		rxci;
+	int		timer_start_1;
+	int		timer_start_2;
+	u32		txci;
+	int		timer_end;
+	int		next_state;
+} state_t;
+
+static state_t lt_states[] = {
+	{ FSM_LT_RECEIVE_RESET, IND_LT_LSL, -1, -1, CMD_NULL, -1, FSM_LT_AWAKE_ERROR },
+	{ FSM_LT_AWAKE_ERROR, IND_LT_AR, 9, -1, CMD_NULL, 9, FSM_LT_EC_TRAINING },
+	{ FSM_LT_TEST, IND_LT_DEAC, -1, -1, CMD_LT_DR, -1, FSM_LT_DEACTIVATED },
+	{ FSM_LT_DEACTIVATED, IND_LT_DI, -1, -1, CMD_NULL, -1, FSM_LT_ALERTING },
+	{ FSM_LT_RESET_FOR_LOOP, IND_LT_DI, 2, -1, CMD_NULL, 2, FSM_LT_ALERTING },
+	{ FSM_LT_ALERTING, IND_LT_DI, 1, 2, CMD_LT_AR, 2, FSM_LT_WAIT_FOR_TN },
+	{ FSM_LT_WAIT_FOR_TN, IND_LT_DI, -1, -1, CMD_NULL, -1, FSM_LT_AWAKE },
+	{ FSM_LT_AWAKE, IND_LT_AR, 1, -1, CMD_LT_AR, -1, FSM_LT_EC_TRAINING },
+	{ FSM_LT_EC_TRAINING, IND_LT_AR, 5, -1, CMD_NULL, 5, FSM_LT_EC_CONVERGED },
+	{ FSM_LT_EC_CONVERGED, IND_LT_ARM, 6, -1, CMD_LT_ARL, 6, FSM_LT_EQ_TRAINING },
+	{ FSM_LT_EQ_TRAINING, IND_LT_ARM, -1, -1, CMD_NULL, -1, FSM_LT_LINE_ACTIVE },
+	{ FSM_LT_LINE_ACTIVE, IND_LT_UAI | IND_LT_FJ, -1, -1, CMD_LT_AR0, -1, FSM_LT_PENDING_TRANSPARENT },
+	{ FSM_LT_PENDING_TRANSPARENT, IND_LT_UAI | IND_LT_FJ, 8, -1, CMD_NULL, 8, FSM_LT_TRANSPARENT },
+	{ FSM_LT_ST_DEACTIVATED, IND_LT_AR | IND_LT_UAI, -1, -1, CMD_LT_AR0, -1, FSM_LT_LINE_ACTIVE },
+	{ FSM_LT_LOSS_OF_SIGNAL, IND_LT_LSL, -1, -1, CMD_LT_RES1, -1, FSM_LT_RECEIVE_RESET },
+	{ FSM_LT_LOSS_OF_SYNC, IND_LT_RSY, -1, -1, CMD_LT_RES1, -1, FSM_LT_TEAR_DOWN_ERROR },
+	{ FSM_LT_TEAR_DOWN_ERROR, IND_LT_EI3 | IND_LT_RSY, -1, -1, CMD_NULL, -1, FSM_LT_RECEIVE_RESET },
+	{ FSM_LT_PENDING_DEACTIVATION, IND_LT_DEAC, 10, -1, CMD_NULL, 10, FSM_LT_TEAR_DOWN },
+	{ FSM_LT_TEAR_DOWN, IND_LT_DEAC, -1, -1, CMD_NULL, -1, FSM_LT_DEACTIVATED },
+};
+
+static state_t nt_states[] = {
+	{ FSM_NT_RECEIVE_RESET, IND_NT_DR, 7, -1, CMD_NT_DI, 7, FSM_NT_PENDING_TIMING },
+	{ FSM_NT_TEST, IND_NT_DR, -1, -1, CMD_NT_DI, -1, FSM_NT_PENDING_TIMING },
+	{ FSM_NT_PENDING_TIMING, IND_NT_DC, 14, -1, CMD_NULL, 14, FSM_NT_DEACTIVATED },
+	{ FSM_NT_DEACTIVATED, IND_NT_DC, -1, -1, CMD_NT_TIMING, -1, FSM_NT_IOM_AWAKED },
+	{ FSM_NT_IOM_AWAKED, IND_NT_PU, -1, -1, CMD_NULL, -1, FSM_NT_ALERTING },
+	{ FSM_NT_ALERTING, IND_NT_DC, 1, 11, CMD_NT_AR, 11, FSM_NT_EC_TRAINING },
+	{ FSM_NT_EC_TRAINING, IND_NT_DC, 12, -1, CMD_NULL, 12, FSM_NT_SYNCHRONIZED_1 },
+	{ FSM_NT_EQ_TRAINING, IND_NT_DC, -1, -1, CMD_NULL, -1, FSM_NT_WAIT_FOR_SF },
+	{ FSM_NT_EQ_TRAINING, IND_NT_DC, -1, -1, CMD_NULL, 1, FSM_NT_PEND_RECEIVE_RES },
+	{ FSM_NT_WAIT_FOR_SF, IND_NT_DC, -1, -1, CMD_NULL, -1, FSM_NT_PEND_RECEIVE_RES },
+	{ FSM_NT_SYNCHRONIZED_1, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_SYNCHRONIZED_2 },
+	{ FSM_NT_SYNCHRONIZED_2, IND_NT_AR, -1, -1, CMD_NT_AI, -1, FSM_NT_WAIT_FOR_ACT },
+	{ FSM_NT_WAIT_FOR_ACT, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_TRANSPARENT },
+	{ FSM_NT_TRANSPARENT, IND_NT_AI, -1, -1, CMD_NULL, -1, FSM_NT_ERROR_ST },
+	{ FSM_NT_ERROR_ST, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_PEND_RECEIVE_RES },
+	{ FSM_NT_PEND_RECEIVE_RES, IND_NT_EI1, 13, -1, CMD_NULL, 13, FSM_NT_RECEIVE_RESET },
+};
+
+#define LT_NUM_STATES	(sizeof (lt_states) / sizeof (*lt_states))
+#define NT_NUM_STATES	(sizeof (nt_states) / sizeof (*nt_states))
+
+static int
+get_state_index (int st, int ltnt)
+{
+	int	i;
+
+	switch (ltnt) {
+		case MODE_LT: for (i = 0; i < LT_NUM_STATES; i ++) {
+			if (lt_states[i].state == st) return i;
+			}
+			break;
+		case MODE_NT: for (i = 0; i < NT_NUM_STATES; i ++) {
+			if (nt_states[i].state == st) return i;
+			}
+			break;
+	}
+	dprintk (KERN_ERR "Unknown state %d\n", st);
+	return 0;
+}
+
+static void
+set_timers (int state_index, int ltnt)
+{
+	switch (ltnt) {
+		case MODE_LT: if (lt_states[state_index].timer_start_1 >= 0) {
+			timers[lt_states[state_index].timer_start_1] = jiffies;
+			}
+			if (lt_states[state_index].timer_start_2 >= 0) {
+				timers[lt_states[state_index].timer_start_2] = jiffies;
+			}
+			break;
+		case MODE_NT: if (nt_states[state_index].timer_start_1 >= 0) {
+			timers[nt_states[state_index].timer_start_1] = jiffies;
+			}
+			if (nt_states[state_index].timer_start_2 >= 0) {
+				timers[nt_states[state_index].timer_start_2] = jiffies;
+			}
+			break;
+	}
+}
+
+static void
+write_txci (int txci, int channels, int card_num)
+{
+	u32	val;
+
+	dprintk (KERN_DEBUG "write_txci (0x%x, %d)\n", txci, card_num);
+	xpds_read_control_register_quiet (card_num, XPDS_MCR_TXCI,
+		&val, XPDS_MAIN);
+
+	val &= ~0xf;
+	val |= TXCI_VAL (txci, channels);
+
+	xpds_write_control_register_quiet (card_num, XPDS_MCR_TXCI,
+		val, XPDS_MAIN);
+}
+
+static void
+set_txci (int state_index, int channels, int card_num, int ltnt)
+{
+	switch (ltnt) {
+		case MODE_LT: if (lt_states[state_index].txci != CMD_NULL) {
+				write_txci (lt_states[state_index].txci, channels, card_num);
+			}
+			break;
+		case MODE_NT: if (nt_states[state_index].txci != CMD_NULL) {
+				write_txci (nt_states[state_index].txci, channels, card_num);
+			}
+			break;
+	}
+}
+
+/* lt */
+static int
+check_state_lt (int state_index, int channels, int card_num)
+{
+	int	i;
+	u32	val;
+
+	if (xpds_data[card_num].rxci_interrupt_received) {
+		/* lock against interrupts? */
+		xpds_data[card_num].rxci_interrupt_received = 0;
+		xpds_read_control_register_quiet (card_num, XPDS_MCR_RXCI,
+			&val, XPDS_MAIN);
+		val &= XPDS_MCR_RXCI__MASK;
+		/* unlock? */
+	} else {
+		val = IND_LT_NULL;
+	}
+
+	if (val != lt_states[state_index].rxci && val != IND_LT_NULL) {
+		dprintk (KERN_DEBUG "%s RXCI = 0x%01x, expected 0x%01x\n",
+			xpds_devs[card_num].name, val, lt_states[state_index].rxci);
+	}
+
+	if (lt_state == FSM_LT_AWAKE) {
+		switch (val) {
+			case IND_LT_UAI:
+				dprintk (KERN_DEBUG "AWAKE/UAI -> PENDING_TRANSPARENT\n");
+				return FSM_LT_PENDING_TRANSPARENT;
+			case IND_LT_ARM:
+				dprintk (KERN_DEBUG "AWAKE/ARM -> EQ_TRAINING\n");
+				return FSM_LT_EQ_TRAINING;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "AWAKE/DI -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			case IND_LT_EI3:
+				dprintk (KERN_DEBUG "AWAKE/EI3 -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			default:
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+		}
+	} else if (lt_state == FSM_LT_DEACTIVATED) {
+		switch (val) {
+			case IND_LT_AR:
+				dprintk (KERN_DEBUG "DEACTIVATED/AR -> AWAKE/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+			case IND_LT_ARM:
+				dprintk (KERN_DEBUG "DEACTIVATED/ARM -> EQ_TRAINING\n");
+				return FSM_LT_EQ_TRAINING;
+			case IND_LT_EI3:
+				dprintk (KERN_DEBUG "DEACTIVATED/EI3 -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "DEACTIVATED/DI -> AWAKE/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+			default:
+				dprintk (KERN_DEBUG "DEACTIVATED/* -> AWAKE/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+		}
+	} else if (lt_state == FSM_LT_EC_TRAINING) {
+		switch (val) {
+			case IND_LT_UAI:
+				dprintk (KERN_DEBUG "EC_TRAINING/UAI -> PENDING_TRANSPARENT\n");
+				return FSM_LT_PENDING_TRANSPARENT;
+			case IND_LT_AI:
+				dprintk (KERN_DEBUG "EC_TRAINING/AI -> TRANSPARENT\n");
+				return FSM_LT_TRANSPARENT;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_EC_CONVERGED) {
+		switch (val) {
+			case IND_LT_UAI:
+				dprintk (KERN_DEBUG "EC_CONVERGED/UAI -> PENDING_TRANSPARENT\n");
+				return FSM_LT_PENDING_TRANSPARENT;
+			case IND_LT_AI:
+				dprintk (KERN_DEBUG "EC_CONVERGED/AI -> TRANSPARENT\n");
+				return FSM_LT_TRANSPARENT;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_EQ_TRAINING) {
+		switch (val) {
+			case IND_LT_UAI:
+				dprintk (KERN_DEBUG "EQ_TRAINING/UAI -> PENDING_TRANSPARENT\n");
+				return FSM_LT_PENDING_TRANSPARENT;
+			case IND_LT_AI:
+				dprintk (KERN_DEBUG "EQ_TRAINING/AI -> TRANSPARENT\n");
+				return FSM_LT_TRANSPARENT;
+			case IND_LT_EI3:
+				dprintk (KERN_DEBUG "EQ_TRAINING/EI3 -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "EQ_TRAINING/DI -> AWAKE/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+			default:
+				if (val != IND_LT_NULL) dprintk (KERN_DEBUG "EQ_TRAINING/0x%01x\n", val);
+				return FSM_LT_EQ_TRAINING;
+		}
+	} else if (lt_state == FSM_LT_PENDING_TRANSPARENT) {
+		switch (val) {
+			case IND_LT_RSY:
+				dprintk (KERN_DEBUG "PENDING_TRANSPARENT/RSY -> WAIT_FOR_TN/RES1\n");
+				write_txci (CMD_LT_RES1, channels, card_num);
+				return FSM_LT_WAIT_FOR_TN;
+			case IND_LT_AI:
+				dprintk (KERN_DEBUG "PENDING_TRANSPARENT/AI -> TRANSPARENT\n");
+				return FSM_LT_TRANSPARENT;
+			case IND_LT_LSL:
+				dprintk (KERN_DEBUG "PENDING_TRANSPARENT/LSL -> RECEIVE_RESET/RES1\n");
+				write_txci (CMD_LT_RES1, channels, card_num);
+				return FSM_LT_RECEIVE_RESET;
+			default:
+				return FSM_LT_PENDING_TRANSPARENT;
+		}
+	} else if (lt_state == FSM_LT_RECEIVE_RESET) {
+		switch (val) {
+			case IND_LT_AR:
+				dprintk (KERN_DEBUG "RECEIVE_RESET/AR -> AWAKE\n");
+				return FSM_LT_AWAKE;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "RECEIVE_RESET/DI -> DEACTIVATED/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_DEACTIVATED;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_TEAR_DOWN_ERROR) {
+		switch (val) {
+			case IND_LT_LSL:
+				dprintk (KERN_DEBUG "TEAR_DOWN_ERROR/LSL -> RECEIVE_RESET\n");
+				return FSM_LT_RECEIVE_RESET;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_TEST) {
+		switch (val) {
+			case IND_LT_DEAC:
+				dprintk (KERN_DEBUG "TEST/DEAC -> DEACTIVATED/DR\n");
+				write_txci (CMD_LT_DR, channels, card_num);
+				return FSM_LT_DEACTIVATED;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "TEST/DI -> DEACTIVATED/DC\n");
+				write_txci (CMD_LT_DC, channels, card_num);
+				return FSM_LT_DEACTIVATED;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_TRANSPARENT) {
+		switch (val) {
+			case IND_LT_RSY:
+				dprintk (KERN_DEBUG "TRANSPARENT/RSY -> TEAR_DOWN_ERROR/RES1\n");
+				write_txci (CMD_LT_RES1, channels, card_num);
+				return FSM_LT_TEAR_DOWN_ERROR;
+			case IND_LT_LSL:
+				dprintk (KERN_DEBUG "TRANSPARENT/LSL -> RECEIVE_RESET\n");
+				return FSM_LT_RECEIVE_RESET;
+			default:
+				break;
+		}
+	} else if (lt_state == FSM_LT_WAIT_FOR_TN) {
+		switch (val) {
+			case IND_LT_AR:
+				dprintk (KERN_DEBUG "WAIT_FOR_TN/AR -> AWAKE/AR\n");
+				write_txci (CMD_LT_AR, channels, card_num);
+				return FSM_LT_AWAKE;
+			case IND_LT_EI3:
+				dprintk (KERN_DEBUG "WAIT_FOR_TN/EI3 -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			case IND_LT_DI:
+				dprintk (KERN_DEBUG "WAIT_FOR_TN/DI -> TEST/RES\n");
+				write_txci (CMD_LT_RES, channels, card_num);
+				return FSM_LT_TEST;
+			default:
+				break;
+		}
+	}
+
+	for (i = state_index;
+		lt_states[i].state == lt_states[state_index].state; i ++) {
+		int	timer_done = 0;
+
+		if (lt_states[i].timer_end < 0) {
+			timer_done = 1;
+		} else if (jiffies - timers[lt_states[i].timer_end] >=
+			timer_limits[lt_states[i].timer_end]) {
+			dprintk (KERN_DEBUG "%s timer %d expired\n",
+				xpds_devs[card_num].name, lt_states[i].timer_end);
+			timer_done = 1;
+		} 
+
+		if (timer_done) return lt_states[i].next_state;
+	}
+	return lt_states[state_index].state;
+}
+
+/* nt */
+static int
+check_state_nt (int state_index, int channels, int card_num)
+{
+	int	i;
+	u32	val;
+
+	if (xpds_data[card_num].rxci_interrupt_received) {
+		/* lock ? */
+		xpds_data[card_num].rxci_interrupt_received = 0;
+		xpds_read_control_register_quiet (card_num, XPDS_MCR_RXCI,
+			&val, XPDS_MAIN);
+		val &= XPDS_MCR_RXCI__MASK;
+		/* unlock ? */
+	} else {
+		val = IND_NT_NULL;
+	}
+
+	if (val != nt_states[state_index].rxci && val != IND_NT_NULL) {
+		dprintk (KERN_DEBUG "%s RXCI = 0x%01x, expected 0x%01x\n",
+			xpds_devs[card_num].name, val, nt_states[state_index].rxci);
+	}
+
+	if (nt_state == FSM_NT_DEACTIVATED) {
+		switch (val) {
+			case IND_NT_AR:
+				dprintk (KERN_DEBUG "DEACTIVATED/AR -> SYNCHRONIZED_2/AI\n");
+				write_txci (CMD_NT_AI, channels, card_num);
+				return FSM_NT_SYNCHRONIZED_2;
+			case IND_NT_PU:
+				dprintk (KERN_DEBUG "DEACTIVATED/PU -> IOM_AWAKED/TIMING\n");
+				write_txci (CMD_NT_TIMING, channels, card_num);
+				return FSM_NT_IOM_AWAKED;
+			default:
+				return FSM_NT_DEACTIVATED;
+		}
+	} else if (nt_state == FSM_NT_IOM_AWAKED) {
+		switch (val) {
+			case IND_NT_DC:
+				dprintk (KERN_DEBUG "IOM_AWAKED/DC -> ALERTING/AR\n");
+				write_txci (CMD_NT_AR, channels, card_num);
+				return FSM_NT_ALERTING;
+			default:
+				dprintk (KERN_DEBUG "IOM_AWAKED/* -> ALERTING/AR\n");
+				write_txci (CMD_NT_AR, channels, card_num);
+				return FSM_NT_IOM_AWAKED;
+		}
+	} else if (nt_state == FSM_NT_ALERTING) {
+		switch (val) {
+			case IND_NT_AR:
+				dprintk (KERN_DEBUG "ALERTING/AR -> SYNCHRONIZED_2/AI\n");
+				write_txci (CMD_NT_AI, channels, card_num);
+				return FSM_NT_SYNCHRONIZED_2;
+			default:
+				return FSM_NT_ALERTING;
+		}
+	} else if (nt_state == FSM_NT_EC_TRAINING) {
+		if (val == IND_NT_AR) {
+			dprintk (KERN_DEBUG "EC_TRAINING/AR -> WAIT_FOR_ACT/AI\n");
+			write_txci (CMD_NT_AI, channels, card_num);
+			return FSM_NT_WAIT_FOR_ACT;
+		}
+	} else if (nt_state == FSM_NT_SYNCHRONIZED_1) {
+		if (val == IND_NT_AR) {
+			dprintk (KERN_DEBUG "SYNCHRONIZED_1/AR -> SYNCHRONIZED_2/AI\n");
+			write_txci (CMD_NT_AI, channels, card_num);
+			return FSM_NT_SYNCHRONIZED_2;
+		}
+	} else if (nt_state == FSM_NT_SYNCHRONIZED_2) {
+		switch (val) {
+			case IND_NT_AR:
+				dprintk (KERN_DEBUG "SYNCHRONIZED_2/AR -> WAIT_FOR_ACT/AI\n");
+				write_txci (CMD_NT_AI, channels, card_num);
+				return FSM_NT_WAIT_FOR_ACT;
+			case IND_NT_AI:
+				dprintk (KERN_DEBUG "SYNCHRONIZED_2/AI -> TRANSPARENT\n");
+				return FSM_NT_TRANSPARENT;
+			case IND_NT_DR:
+				dprintk (KERN_DEBUG "SYNCHRONIZED_2/DR -> PEND_DEACT_ST/DI\n");
+				write_txci (CMD_NT_DI, channels, card_num);
+				return FSM_NT_PEND_DEACT_ST;
+			default:
+				if (val != IND_NT_NULL) dprintk (KERN_DEBUG "SYNCHRONIZED_2/0x%01x\n", val);
+				return FSM_NT_SYNCHRONIZED_2;
+		}
+	} else if (nt_state == FSM_NT_WAIT_FOR_ACT) {
+		switch (val) {
+			case IND_NT_AI:
+				dprintk (KERN_DEBUG "WAIT_FOR_ACT/AI -> TRANSPARENT\n");
+				return FSM_NT_TRANSPARENT;
+			case IND_NT_DR:
+				write_txci (CMD_NT_DI, channels, card_num);
+				dprintk (KERN_DEBUG "WAIT_FOR_ACT/DI -> PEND_DEACT_ST\n");
+				return FSM_NT_PEND_DEACT_ST;
+			case IND_NT_AR:
+				dprintk (KERN_DEBUG "WAIT_FOR_ACT/AR -> WAIT_FOR_ACT/AI\n");
+				write_txci (CMD_NT_AI, channels, card_num);
+				return FSM_NT_WAIT_FOR_ACT;
+			default:
+				/* write_txci (CMD_NT_AI, channels, card_num); */
+				return FSM_NT_WAIT_FOR_ACT;
+		}
+	} else if (nt_state == FSM_NT_TRANSPARENT) {
+		switch (val) {
+			case IND_NT_AR:
+				dprintk (KERN_DEBUG "TRANSPARENT/AR -> ERROR_ST\n");
+				return FSM_NT_ERROR_ST;
+			case IND_NT_DR:
+				write_txci (CMD_NT_DI, channels, card_num);
+				dprintk (KERN_DEBUG "TRANSPARENT/DR -> PEND_DEACT_ST\n");
+				return FSM_NT_PEND_DEACT_ST;
+			default:
+				return FSM_NT_TRANSPARENT;
+		}
+	}
+
+	if (val == IND_NT_AR) {
+		dprintk (KERN_DEBUG "*/AR -> SYNCHRONIZED_2/AI\n");
+		write_txci (CMD_NT_AI, channels, card_num);
+		return FSM_NT_SYNCHRONIZED_2;
+	}
+
+	for (i = state_index;
+		nt_states[i].state == nt_states[state_index].state; i ++) {
+		int	timer_done = 0;
+
+		if (nt_states[i].timer_end < 0) {
+			timer_done = 1;
+		} else if (jiffies - timers[nt_states[i].timer_end] >=
+			timer_limits[nt_states[i].timer_end]) {
+			dprintk (KERN_DEBUG "%s timer %d expired\n",
+				xpds_devs[card_num].name, nt_states[i].timer_end);
+			timer_done = 1;
+		} 
+
+		if (timer_done) return nt_states[i].next_state;
+	}
+	return nt_states[state_index].state;
+}
+
+/* lt */
+int
+xpds_fsm_lt (int card_num, int channels, int guard_time)
+{
+	u32	guard;
+
+	dprintk (KERN_DEBUG "xpds_fsm_lt (%d) (LT - line termination)\n", card_num);
+
+	guard = jiffies + guard_time * HZ;
+
+	/*
+	 * Reset
+	 */
+	xpds_data[card_num].rxci_interrupt_received = 0;
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI,
+		TXCI_VAL (CMD_LT_RES, channels), XPDS_MAIN);
+	/*
+	 * Unreset
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI,
+		TXCI_VAL (CMD_LT_DR, channels), XPDS_MAIN);
+
+	lt_state = FSM_LT_TEST;
+
+	dprintk (KERN_DEBUG "waiting for RXCI interrupt\n");
+	while (! xpds_data[card_num].rxci_interrupt_received &&
+		jiffies < guard) {
+		schedule ();
+	}
+
+	guard = jiffies + guard_time * HZ;
+
+	/*
+	 * For each state set the timers, set the TXCI value,
+	 * then loop, checking for transition.  We want to
+	 * get into the transparent state.
+	 */
+	while (lt_state != FSM_LT_TRANSPARENT) {
+		int	state_index, new_state;
+
+		dprintk (KERN_DEBUG "%s state %d\n", xpds_devs[card_num].name, lt_state);
+		state_index = get_state_index (lt_state, MODE_LT);
+		set_timers (state_index, MODE_LT);
+		set_txci (state_index, channels, card_num, MODE_LT);
+
+		do {
+			new_state = check_state_lt (state_index, channels, card_num);
+			if (guard_time > 0 && jiffies >= guard) {
+				dprintk (KERN_ERR "%s FSM guard timer (%d seconds) expired in state %d\n", xpds_devs[card_num].name, guard_time, lt_state);
+				return -ETIME;
+			}
+			schedule ();
+		} while (new_state == lt_state);
+
+		lt_state = new_state;
+		guard = jiffies + guard_time * HZ;
+	}
+
+	dprintk (KERN_DEBUG "state %d == TRANSPARENT\n", lt_state);
+
+	return 0;
+}
+
+/* nt */
+int
+xpds_fsm_nt (int card_num, int channels, int guard_time)
+{
+	u32	guard;
+
+	dprintk (KERN_DEBUG "xpds_fsm_nt (%d) (NT - network termination)\n", card_num);
+
+	guard = jiffies + guard_time * HZ;
+
+	/*
+	 * Reset
+	 */
+	xpds_data[card_num].rxci_interrupt_received = 0;
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI,
+		TXCI_VAL (CMD_NT_RESET, channels), XPDS_MAIN);
+	/*
+	 * Unreset
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI,
+		TXCI_VAL (CMD_NT_DI, channels), XPDS_MAIN);
+
+	nt_state = FSM_NT_TEST;
+
+	dprintk (KERN_DEBUG "waiting for RXCI interrupt\n");
+	while ( ! xpds_data[card_num].rxci_interrupt_received &&
+		jiffies < guard) {
+		schedule ();
+	}
+
+	guard = jiffies + guard_time * HZ;
+
+	/*
+	 * For each state set the timers, set the TXCI value,
+	 * then loop, checking for transition.  We want to
+	 * get into the transparent state.
+	 */
+	while (nt_state != FSM_NT_TRANSPARENT) {
+		int	state_index, new_state;
+
+		dprintk (KERN_DEBUG "%s state %d\n", xpds_devs[card_num].name, nt_state);
+		state_index = get_state_index (nt_state, MODE_NT);
+		set_timers (state_index, MODE_NT);
+		set_txci (state_index, channels, card_num, MODE_NT);
+
+		do {
+			new_state = check_state_nt (state_index, channels, card_num);
+			if (guard_time > 0 && jiffies >= guard) {
+				dprintk (KERN_ERR "%s FSM guard timer (%d seconds) expired in state %d\n", xpds_devs[card_num].name, guard_time, nt_state);
+				return -ETIME;
+			}
+			schedule ();
+		} while (new_state == nt_state);
+
+		nt_state = new_state;
+		guard = jiffies + guard_time * HZ;
+	}
+
+	dprintk (KERN_DEBUG "state %d == TRANSPARENT\n", nt_state);
+
+	return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-fsm.h linux/drivers/net/xpds/xpds-fsm.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-fsm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-fsm.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,12 @@
+/*
+ * Copyright 1998, 1999, 2000 Xpeed, Inc.
+ * xpds-fsm.h, $Revision: 1.2 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifndef XPDS_FSM_H
+#define XPDS_FSM_H 1
+
+int xpds_fsm_nt (int card_num, int channels, int guard_time);
+int xpds_fsm_lt (int card_num, int channels, int guard_time);
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-reg.h linux/drivers/net/xpds/xpds-reg.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-reg.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-reg.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,220 @@
+/*
+ * Copyright 1998, 1999, 2000 Xpeed, Inc.
+ * xpds-reg.h, $Revision: 1.2 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifndef XPDS_REG_H
+#define XPDS_REG_H 1
+
+/*
+ * Main control registers
+ */
+#define XPDS_MCR_CONFIG		0x00
+#define XPDS_MCR_TEST		0x04
+#define XPDS_MCR_MASK_SET	0x08
+#define XPDS_MCR_MASK_CLR	0x0c
+#define XPDS_MCR_INT_SET	0x10
+#define XPDS_MCR_INT_CLR	0x14
+#define XPDS_MCR_GPIO_SET	0x18
+#define XPDS_MCR_GPIO_CLR	0x1c
+#define XPDS_MCR_TXCFG		0x20
+#define XPDS_MCR_RXCFG		0x24
+#define XPDS_MCR_PACKH		0x28
+#define XPDS_MCR_PACKL		0x2c
+#define XPDS_MCR_TXCI		0x30
+#define XPDS_MCR_RXCI		0x34
+#define XPDS_MCR_DBG_ADDR	0x38
+#define XPDS_MCR_DBG		0x3c
+
+#define XPDS_MCR_INT_MASK_SET	XPDS_MCR_MASK_SET
+#define XPDS_MCR_INT_MASK_CLR	XPDS_MCR_MASK_CLR
+#define XPDS_MCR_INT_STATUS_SET	XPDS_MCR_INT_SET
+#define XPDS_MCR_INT_STATUS_CLR	XPDS_MCR_INT_CLR
+#define XPDS_MCR_TX_CONFIG	XPDS_MCR_TXCFG
+#define XPDS_MCR_RX_CONFIG	XPDS_MCR_RXCFG
+
+#define XPDS_MCR_CONFIG__REVISION_MASK	0xe0
+#define XPDS_MCR_CONFIG__MODE_NORMAL	0x00
+#define XPDS_MCR_CONFIG__MODE_LOOPBACK	0x18
+#define XPDS_MCR_CONFIG__CONFIG_MASK	0x07
+
+#define XPDS_MCR_TEST__BIT_CLK_EN	0x10
+#define XPDS_MCR_TEST__CLK_DIV_4	0x00
+#define XPDS_MCR_TEST__CLK_DIV_8	0x02
+#define XPDS_MCR_TEST__CLK_DIV_16	0x04
+#define XPDS_MCR_TEST__CLK_DIV_32	0x06
+#define XPDS_MCR_TEST__CLK_DIV_64	0x08
+#define XPDS_MCR_TEST__SEL_PMD_CLK	0x01
+
+#define XPDS_MCR_MASK__RX_FIFO		0x01
+#define XPDS_MCR_MASK__TX_FIFO		0x02
+#define XPDS_MCR_MASK__RX_DMA		0x04
+#define XPDS_MCR_MASK__TX_DMA		0x08
+#define XPDS_MCR_MASK__PCI_ERROR	0x10
+#define XPDS_MCR_MASK__EXT		0x20
+#define XPDS_MCR_MASK__RXCI		0x40
+
+#define XPDS_MCR_INT__RX_FIFO		0x01
+#define XPDS_MCR_INT__TX_FIFO		0x02
+#define XPDS_MCR_INT__RX_DMA		0x04
+#define XPDS_MCR_INT__TX_DMA		0x08
+#define XPDS_MCR_INT__PCI_ERROR		0x10
+#define XPDS_MCR_INT__EXT		0x20
+#define XPDS_MCR_INT__RXCI		0x40
+#define XPDS_MCR_INT__INT		0x80
+
+#define XPDS_MCR_GPIO__GP_MASK		0x0f
+#define XPDS_MCR_GPIO__GP_SEPROM_CS	0x01
+#define XPDS_MCR_GPIO__GP_SEPROM_SK	0x02
+#define XPDS_MCR_GPIO__GP_SEPROM_DO	0x04
+#define XPDS_MCR_GPIO__GP_SEPROM_DI	0x08
+#define XPDS_MCR_GPIO__GP_FPGA_NT	0x00
+#define XPDS_MCR_GPIO__GP_FPGA_LT	0x01
+#define XPDS_MCR_GPIO__GP_ASIC_ACT_LED	0x02
+#define XPDS_MCR_GPIO__GP_ASIC_LINK_LED	0x08
+#define XPDS_MCR_GPIO__GP_SDSL_ACT_LED	0x08
+#define XPDS_MCR_GPIO__GP_FPGA_LT_NT	0x01
+#define XPDS_MCR_GPIO__GP_FPGA_TX_LED	0x02
+#define XPDS_MCR_GPIO__GP_FPGA_RX_LED	0x04
+#define XPDS_MCR_GPIO__OE_MASK		0xf0
+#define XPDS_MCR_GPIO__OE_SEPROM_CS	0x10
+#define XPDS_MCR_GPIO__OE_SEPROM_SK	0x20
+#define XPDS_MCR_GPIO__OE_SEPROM_DO	0x40
+#define XPDS_MCR_GPIO__OE_SEPROM_DI	0x80
+#define XPDS_MCR_GPIO__OE_ASIC_ACT_LED	0x20
+#define XPDS_MCR_GPIO__OE_ASIC_LINK_LED	0x80
+#define XPDS_MCR_GPIO__OE_SDSL_ACT_LED	0x80
+#define XPDS_MCR_GPIO__OE_FPGA_LT_NT	0x10
+#define XPDS_MCR_GPIO__OE_FPGA_TX_LED	0x20
+#define XPDS_MCR_GPIO__OE_FPGA_RX_LED	0x40
+
+#define XPDS_MCR_TXCFG__TRANS		0x01
+#define XPDS_MCR_TXCFG__ENABLE		0x02
+#define XPDS_MCR_TXCFG__FILL		0x04
+#define XPDS_MCR_TXCFG__SKIP_CRC	0x08
+#define XPDS_MCR_TXCFG__CRC		0x10
+#define XPDS_MCR_TXCFG__BIT_REV_CRC	0x20
+#define XPDS_MCR_TXCFG__BIT_REV_DATA	0x40
+#define XPDS_MCR_TXCFG__SWAP_CRC	0x80
+
+#define XPDS_MCR_RXCFG__TRANS		0x01
+#define XPDS_MCR_RXCFG__ENABLE		0x02
+#define XPDS_MCR_RXCFG__IGNORE_CRC	0x04
+#define XPDS_MCR_RXCFG__NO_CRC		0x08
+#define XPDS_MCR_RXCFG__CRC		0x10
+#define XPDS_MCR_RXCFG__BIT_REV_CRC	0x20
+#define XPDS_MCR_RXCFG__BIT_REV_DATA	0x40
+#define XPDS_MCR_RXCFG__SWAP_CRC	0x80
+
+#define XPDS_MCR_TXCI__PMD_ENABLE	0x80
+#define XPDS_MCR_TXCI__PMD_CONFIG_MASK	0x60
+#define XPDS_MCR_TXCI__PMD_CONFIG_B1	0x00
+#define XPDS_MCR_TXCI__PMD_CONFIG_B2	0x20
+#define XPDS_MCR_TXCI__PMD_CONFIG_B1B2	0x40
+#define XPDS_MCR_TXCI__PMD_CONFIG_B1B2D	0x60
+#define XPDS_MCR_TXCI__PMD_RESQ		0x10
+#define XPDS_MCR_TXCI__MASK		0x0f
+
+#define XPDS_MCR_RXCI__PMD_ACTIVE	0x40
+#define XPDS_MCR_RXCI__MASK		0x0f
+
+/*
+ * FIFO control registers
+ */
+#define XPDS_FCR_CONFIG		0x00
+#define XPDS_FCR_INC		0x04
+#define XPDS_FCR_STAT		0x08
+#define XPDS_FCR_TAG		0x0c
+
+#define XPDS_FCR_STATUS		XPDS_FCR_STAT
+
+#define XPDS_FCR_CONFIG__RESET		0x01
+#define XPDS_FCR_CONFIG__PTR_MASK	0x06
+
+#define XPDS_FCR_INC__NEXT	0x01
+
+#define XPDS_FCR_STAT__LAST		0x80
+#define XPDS_FCR_STAT__SIZE_MASK	0x1f
+
+#define XPDS_FCR_TAG__MASK	0x0f
+
+/*
+ * DMA
+ */
+#define XPDS_DMA_CONFIG		0x00
+#define XPDS_DMA_GO		0x04
+#define XPDS_DMA_DESC		0x08
+#define XPDS_DMA_SET_MASK	0x0c
+#define XPDS_DMA_CLR_MASK	0x10
+#define XPDS_DMA_SET_STAT	0x14
+#define XPDS_DMA_CLR_STAT	0x18
+#define XPDS_DMA_STAT_ADDR	0x1c
+#define XPDS_DMA_BUF_ADDR	0x20
+#define XPDS_DMA_DBG_ADDR	0x24
+#define XPDS_DMA_DBG		0x28
+
+#define XPDS_DMA_MASK_SET	XPDS_DMA_SET_MASK
+#define XPDS_DMA_MASK_CLR	XPDS_DMA_CLR_MASK
+#define XPDS_DMA_STAT_SET	XPDS_DMA_SET_STAT
+#define XPDS_DMA_STAT_CLR	XPDS_DMA_CLR_STAT
+
+#define XPDS_DMA_CONFIG__TEST_ENABLE	0x08
+#define XPDS_DMA_CONFIG__BURST_ENABLE	0x04
+#define XPDS_DMA_CONFIG__RESET		0x02
+#define XPDS_DMA_CONFIG__ENABLE		0x01
+
+#define XPDS_DMA_GO__HUNT		0x01
+
+#define XPDS_DMA_DESC__SW_GO			0x80000000
+#define XPDS_DMA_DESC__HW_GO			0x40000000
+#define XPDS_DMA_DESC__NEXT_VALID		0x20000000
+#define XPDS_DMA_DESC__PACKET_TAG_MASK		0x000f0000
+#define XPDS_DMA_DESC__PACKET_TAG_OK		0x00000000
+#define XPDS_DMA_DESC__PACKET_TAG_CRC		0x00010000
+#define XPDS_DMA_DESC__PACKET_TAG_BOUNDARY_VIOLATION	0x00020000
+#define XPDS_DMA_DESC__PACKET_TAG_LONG		0x00040000
+#define XPDS_DMA_DESC__PACKET_TAG_ABORT		0x00050000
+#define XPDS_DMA_DESC__PACKET_TAG_OVERFLOW	0x00080000
+#define XPDS_DMA_DESC__PACKET_LENGTH_MASK	0x0000ffff
+
+#define XPDS_DMA_MASK__FLOW		0x20
+#define XPDS_DMA_MASK__CRC		0x10
+#define XPDS_DMA_MASK__LONG		0x08
+#define XPDS_DMA_MASK__ABORT		0x04
+#define XPDS_DMA_MASK__DONE		0x01
+
+#define XPDS_DMA_STAT__FLOW		0x20
+#define XPDS_DMA_STAT__CRC		0x10
+#define XPDS_DMA_STAT__LONG		0x08
+#define XPDS_DMA_STAT__ABORT		0x04
+#define XPDS_DMA_STAT__DONE		0x01
+
+/*
+ * Auxiliary registers (8032 for SDSL)
+ */
+#define XPDS_AUX_CONTROL		0x00
+#define XPDS_AUX_WRITE_ADDR		0x04
+#define XPDS_AUX_WRITE_DATA		0x08
+#define XPDS_AUX_READ_DATA		0x04
+#define XPDS_AUX_READ_ADDR		0x08
+#define XPDS_AUX_MAILBOX_DATA		0x0c
+
+#define XPDS_AUX_MAILBOX__HANDSHAKE			0x80
+#define XPDS_AUX_MAILBOX__WRITE_BOOTDATA		0x00
+#define XPDS_AUX_MAILBOX__WRITE_MAC_ADDR		0x01
+#define XPDS_AUX_MAILBOX__WRITE_HARDWARE_VERSION	0x02
+#define XPDS_AUX_MAILBOX__WRITE_FIRMWARE_VERSION	0x03
+#define XPDS_AUX_MAILBOX__WRITE_PHYSICAL_MODE		0x04
+#define XPDS_AUX_MAILBOX__READ_MAC_ADDR			0x05
+#define XPDS_AUX_MAILBOX__READ_HARDWARE_VERSION		0x06
+#define XPDS_AUX_MAILBOX__READ_FIRMWARE_VERSION		0x07
+#define XPDS_AUX_MAILBOX__READ_PHYSICAL_MODE		0x08
+#define XPDS_AUX_MAILBOX__WRITE_TEST_MODE		0x09
+#define XPDS_AUX_MAILBOX__CLEAR_IRQ			0x0a
+#define XPDS_AUX_MAILBOX__READ_PHYSICAL_STATE		0x0b
+
+#define XPDS_AUX_MAILBOX__NIC_WRITE_DEBUG_PORT		0x0d
+#define XPDS_AUX_MAILBOX__NIC_WRITE_IRQ_TYPE		0x0e
+#define XPDS_AUX_MAILBOX__NIC_WRITE_EXIT_CODE		0x0f
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-sdsl.c linux/drivers/net/xpds/xpds-sdsl.c
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-sdsl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-sdsl.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,965 @@
+/*
+ * Copyright 1999, 2000 Xpeed, Inc.
+ * xpds-sdsl.c, $Revision: 1.7 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifdef DEBUG
+#define	dprintk		if (xpds_debug_level & DEBUG_MAIN) printk
+#define	ddprintk	if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) == (DEBUG_MAIN | DEBUG_DETAILED)) printk
+#else
+#define	dprintk		if (0) printk
+#define	ddprintk	if (0) printk
+#endif
+
+#ifndef __KERNEL__
+#define __KERNEL__ 1
+#endif
+#ifndef MODULE
+#define MODULE 1
+#endif
+
+#define __NO_VERSION__ 1
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h>
+
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include "xpds-encap-fr.h"
+#include <linux/if_arp.h>
+
+#include <linux/tqueue.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>  
+
+#if ! defined (CONFIG_PCI)
+#error "CONFIG_PCI is not defined"
+#endif
+
+#include "xpds.h"
+#include "xpds-reg.h"
+
+#define	AUX_CONTROL	0x0
+#define	AUX_WRITE_ADDR	0x4
+#define	AUX_WRITE_DATA	0x8
+#define	AUX_READ_DATA	0x4
+#define	AUX_READ_ADDR	0x8
+#define	AUX_MAILBOX	0xc
+
+#define AUX_CONTROL__BUSY	0x08
+
+#define ASIC_FLASHSIZE	0x8000
+#define FPGA_FLASHSIZE	0x10000
+
+/*
+ * Using __inline__ produces bugs.
+ */
+#define __inline__
+
+#define	aux_busy_wait(card_num,n) \
+	do { \
+		u32	wait_until_abw = jiffies + (n) * HZ; \
+		for (;;) { \
+			u32	val_d; \
+			int	rc_abw; \
+			rc_abw = xpds_read_control_register_quiet (card_num, AUX_CONTROL, &val_d, AUX); \
+			if (rc_abw > 0) return rc_abw; \
+			if (! (val_d & AUX_CONTROL__BUSY) ) break; \
+			schedule_if_no_interrupt (card_num); \
+			if (jiffies > wait_until_abw) return 5; \
+		} \
+	} while (0)
+
+#define flash_addr_control(card_num,addr,ctrl) \
+	do { \
+		int	rc_fac; \
+		rc_fac = xpds_write_control_register_quiet (card_num, AUX_WRITE_ADDR, (addr), AUX); \
+		if (rc_fac > 0) return rc_fac; \
+		rc_fac = xpds_write_control_register_quiet (card_num, AUX_CONTROL, (ctrl), AUX); \
+		if (rc_fac > 0) return rc_fac; \
+		aux_busy_wait (card_num, 1); \
+	} while (0)
+
+#define flash_data_control(card_num,data,ctrl) \
+	do { \
+		int	rc_fdc; \
+		rc_fdc = xpds_write_control_register_quiet (card_num, AUX_WRITE_DATA, (data), AUX); \
+		if (rc_fdc > 0) return rc_fdc; \
+		rc_fdc = xpds_write_control_register_quiet (card_num, AUX_CONTROL, (ctrl), AUX); \
+		if (rc_fdc > 0) return rc_fdc; \
+		aux_busy_wait (card_num, 1); \
+	} while (0)
+
+__inline__ int
+xpds_reset_flash (int card_num)
+{
+	flash_addr_control (card_num, 0, 0x106);
+	flash_data_control (card_num, 0xff, 0x105);
+	return 0;
+}
+
+__inline__ static int
+set_flash_read_mode (int card_num)
+{
+	flash_addr_control (card_num, 0, 0x106);
+	flash_data_control (card_num, 0, 0x105);
+	return 0;
+}
+
+__inline__ int
+xpds_read_flash_byte (int card_num, u32 address, u8 *valuep)
+{
+	u32	val;
+	int	rc;
+
+	rc = set_flash_read_mode (card_num);
+	if (rc > 0) return rc;
+
+	address &= 0xffff;
+
+	flash_addr_control (card_num, address, 0x106);
+	flash_data_control (card_num, 0xaa, 0x104);
+
+	/* will this work on a big-endian computer? */
+	rc = xpds_read_control_register_quiet (card_num,
+		AUX_READ_DATA, &val, AUX);
+	if (rc > 0) return rc;
+
+	*valuep = val;
+
+	ddprintk ("flash[%04x]->%02x\n", address, *valuep);
+	return 0;
+}
+
+__inline__ int
+xpds_write_flash_byte (int card_num, u32 address, u8 value)
+{
+	int	rc, done, count;
+	u8	rval;
+
+	rc = xpds_reset_flash (card_num);
+	if (rc > 0) return rc;
+
+	address &= 0xffff;
+
+	for (count = 0, done = 0; ! done && count < 100; ) {
+
+		/* program setup command */
+		flash_addr_control (card_num, address, 0x106);
+		flash_data_control (card_num, 0x40, 0x105);
+
+		/* program command */
+		flash_addr_control (card_num, address, 0x106);
+		flash_data_control (card_num, value, 0x105);
+
+		udelay (10);
+
+		/* program verify command */
+		flash_addr_control (card_num, address, 0x106);
+		flash_data_control (card_num, 0xc0, 0x105);
+
+		udelay (6);
+
+		/* read back */
+		rc = xpds_read_flash_byte (card_num, address, &rval);
+
+		if (rval == value) {
+			done = 1;
+		} else {
+			count ++;
+		}
+	}
+	if (! done) {
+		printk (KERN_ERR "write_flash_byte (%d, %04lx, %02x) failed (rval = %02x)\n", card_num, (unsigned long) address, value, rval);
+		return 6;
+	}
+
+	ddprintk ("%02x->flash[%04x]\n", value, address);
+
+	return 0;
+}
+
+__inline__ int
+xpds_verify_flash (int card_num, u8 *buffer, u8 expected_byte, int length)
+{
+	int	i;
+	int	rc;
+
+	for (i = 0; i < length; i ++) {
+		u8	byte, ex_byte;
+
+		rc = xpds_read_flash_byte (card_num, i, &byte);
+		ex_byte = ((buffer != NULL) ? buffer[i] : expected_byte);
+		if (byte != ex_byte) {
+			printk (KERN_ERR "xpds_verify_flash() failed at %04x (got %02x, expected %02x)\n", i, byte, ex_byte);
+			return 6;
+		}
+	}
+	return -1;
+}
+
+__inline__ int
+xpds_erase_flash (int card_num)
+{
+	int	i, verified = 0, done = 0, rc;
+	int	flashsize;
+
+	/* write all zeros */
+	flashsize = xpds_data[card_num].is_fpga ?
+		FPGA_FLASHSIZE : ASIC_FLASHSIZE;
+	for (i = 0; i < flashsize; i ++) {
+		rc = xpds_write_flash_byte (card_num, i, 0);
+		if (rc > 0) {
+			printk (KERN_ERR "%s: writing 0 to flash byte %04x during erase failed\n", xpds_devs[card_num].name, i);
+			return rc;
+		}
+		schedule ();
+	}
+
+	rc = xpds_verify_flash (card_num, NULL, 0, flashsize);
+	if (rc > 0) return rc;
+
+	/* bulk erase 1000 times */
+
+	for (i = 0; i < 1000; ) {
+		u32	wait_until;
+		int	j;
+
+		/* erase setup */
+		flash_addr_control (card_num, 0, 0x106);
+		flash_data_control (card_num, 0x20, 0x105);
+
+		/* erase */
+		flash_addr_control (card_num, 0, 0x106);
+		flash_data_control (card_num, 0x20, 0x105);
+
+		/* delay 10ms */
+		wait_until = jiffies + 10 * HZ / 1000;
+		while (jiffies < wait_until) {
+			schedule ();
+		}
+
+		/* verify that each byte is 0xff */
+		for (j = verified; j < flashsize; j ++) {
+			u32	val;
+			u8	byte;
+
+			/* erase verify */
+			flash_addr_control (card_num, (j & 0xffff), 0x106);
+			flash_data_control (card_num, 0xa0, 0x105);
+			udelay (6);
+
+			/* read data */
+			flash_addr_control (card_num, (j & 0xffff), 0x106);
+			flash_data_control (card_num, 0, 0x104);
+
+			rc = xpds_read_control_register (card_num, AUX_READ_DATA, &val, AUX);
+			byte = val;		
+			if (rc > 0 || byte != 0xff) {
+				done = 0;
+				verified = j;
+				i ++;
+				break;
+			} else {
+				done = 1;
+			}
+		}
+		/*
+		printk ("i = %d, verified = %04x, done = %d\n",
+			i, verified, done);
+		*/
+
+		if (done) break;
+	}
+	if (i >= 1000) {
+		ddprintk ("xpds_erase_flash() failed\n");
+		return 6;
+	}
+
+	xpds_reset_flash (card_num);
+	ddprintk ("xpds_erase_flash() succeeded\n");
+
+	rc = xpds_verify_flash (card_num, NULL, 0xff, flashsize);
+	if (rc > 0) {
+		printk (KERN_ERR "xpds_verify_flash () during erase failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * IRQ types for value returned by using XPDS_MBX_WRITE_IRQTYPE 
+ * with xpds_mailbox_read().
+ */
+typedef enum {      /* Codes for physical layer i/o */
+	XPDS_SDSL_CMD_FAILED,            /* Physical layer command failed */
+	XPDS_SDSL_CMD_COMPLETED,         /* Physical layer command completed successfully */
+	XPDS_SDSL_CMD_TIMEOUT,           /* Physical layer command timed out */
+	XPDS_SDSL_FLASH_ERASE_ERROR,     /* Flash could not be erased */
+	XPDS_SDSL_FLASH_ZERO_ERROR,      /* Flash could not be zeroed out */
+	XPDS_SDSL_FLASH_WRITE_ERROR,     /* Flash could not be written */
+	XPDS_SDSL_AUX_MODE_PCI,          /* PCI is the aux bus master */
+	XPDS_SDSL_AUX_MODE_NIC,          /* NIC is the aux bus master */
+	XPDS_SDSL_AUX_MODE_BOTH,         /* Both can be aux master */
+	XPDS_SDSL_RAM_ERROR,             /* RAM failed a self-test */
+	XPDS_SDSL_IRQTYPE_STATE_CHANGE,  /* Physical layer has gone into an important state */
+	XPDS_SDSL_STATE_BOOTED,          /* PL State: Booted */ 
+	XPDS_SDSL_TESTMODE_NORMAL_OPERATION,         /* Return to normal operation */
+	XPDS_SDSL_TESTMODE_EXTERNAL_ANALOG_LOOPBACK, /* Transmit then receive echo */
+	XPDS_SDSL_TESTMODE_FOUR_LEVEL_SCR,           /* Transmit a continuous stream of 4 level scrambled ones */
+	XPDS_SDSL_TESTMODE_TWO_LEVEL_SCR,            /* Transmit a continuous stream of 2 level scrambled ones */
+	XPDS_SDSL_TESTMODE_INTERNAL_ANALOG_LOOPBACK, /* Loopback bypassing pins */  
+	XPDS_SDSL_TESTMODE_FORCE_LINKUP,             /* Force link up so driver can start */                               
+	XPDS_SDSL_MAX                                /* Not a code - Used for bounds checking */
+} xpds_sdsl_codes_t;
+
+typedef enum { /* Maximum 128 types total */
+                                       /* BEGIN PCI MAILBOX */
+    XPDS_MBX_WRITE_BOOTDATA,             /* Write bootdata from the mailbox */
+    XPDS_MBX_WRITE_MACADDR,              /* Write the mac address from the mailbox */
+    XPDS_MBX_WRITE_HWVER,                /* Write the hardware version from the mailbox */
+    XPDS_MBX_WRITE_FWVER,                /* Write the firmware version from the mailbox */
+    XPDS_MBX_WRITE_PHYSMODE,             /* Write the physical layer mode */
+    XPDS_MBX_READ_MACADDR,               /* Send the mac address to the mailbox */
+    XPDS_MBX_READ_HWVER,                 /* Send the hardware version to the mailbox */
+    XPDS_MBX_READ_FWVER,                 /* Send the firmware version to the mailbox */
+    XPDS_MBX_READ_PHYSMODE,              /* Write the physical layer mode */
+    XPDS_MBX_WRITE_TESTMODE,             /* Put the rs8973 into a test mode */
+    XPDS_MBX_CLEAR_IRQ,                  /* Clear the interrupt being generated */
+    XPDS_MBX_READ_PHYS_STATE,            /* Read the physical layer state */
+    XPDS_MBX_WRITE_MFGDATE,		 /* 4 bytes */
+    XPDS_MBX_READ_MFGDATE,
+    XPDS_MBX_READ_SERIALNUMBER,		 /* 16 bytes */
+    XPDS_MBX_WRITE_SERIALNUMBER,
+    XPDS_MBX_READ_STAGE,
+    XPDS_MBX_START_BITPUMP,
+    XPDS_MBX_START_BITPUMP_FOR_MFG,
+    XPDS_MBX_READ_PHYSMODE_IN_FLASH,
+    XPDS_MBX_READ_PHYS_QUALITY,
+    XPDS_MBX_PCIMASTER_MAXTYPE,          /* Types below this value are for the PCI mailbox only */
+                                       /* BEGIN NIC MAILBOX (r/w from NIC perspective) */
+    XPDS_MBX_WRITE_DEBUGPORT = 64,       /* Get the NIC supplied terminal output from the mailbox */
+    XPDS_MBX_WRITE_IRQTYPE,              /* Get the type of irq being generated */
+    XPDS_MBX_WRITE_EXITCODE,             /* Write the completion code */        
+    XPDS_MBX_NICMASTER_MAXTYPE           /* Types below this value are for NIC mailbox only */  
+} xpds_mbx_t;
+
+/* MAILBOX BITMASKS */
+#define  XPDS_MBX_PCI_HANDSHAKE_BIT         0x00000080
+#define  XPDS_MBX_NIC_HANDSHAKE_BIT         0x00800000
+#define  XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED 0x00000080
+
+typedef union {
+	u8	bytes[4];
+	u32	word;
+} mailbox_t;
+
+#define XPDS_MBX_PCI_FLAGS	0
+#define XPDS_MBX_PCI_DATA	1
+#define XPDS_MBX_NIC_FLAGS	2
+#define XPDS_MBX_NIC_DATA	3
+
+#define mailbox_busy_wait(card_num,n,bit) \
+	do { \
+		u32	wait_until_mbw = jiffies + (n) * HZ / 1000; \
+		for (;;) { \
+			u32	val_mbw; \
+			xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_mbw, AUX); \
+			if (val_mbw & (bit)) break; \
+			schedule_if_no_interrupt (card_num); \
+			if (jiffies > wait_until_mbw) { \
+				printk (KERN_ERR "(val_mbw=%x,bit=%x)", val_mbw, bit);\
+				return 5; \
+			} \
+		} \
+	} while (0)
+
+#define mailbox_busy_wait_not(card_num,n,bit) \
+	do { \
+		u32	wait_until_mbw = jiffies + (n) * HZ / 1000; \
+		for (;;) { \
+			u32	val_mbw; \
+			xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_mbw, AUX); \
+			if (! (val_mbw & (bit))) break; \
+			schedule_if_no_interrupt (card_num); \
+			if (jiffies > wait_until_mbw) { \
+				printk (KERN_ERR "(val_mbw=%x,!bit=%x)", val_mbw, bit);\
+				return 5; \
+			} \
+		} \
+	} while (0)
+
+
+static mailbox_t	*mailbox = NULL;
+/*
+ * Timeouts in milliseconds.
+ * Need a longer timeout for use with Copper Mountain when getting the
+ * speed assigned by the DSLAM.
+ */
+#define MAILBOX_WRITE_TIMEOUT	(xpds_data[card_num].sdsl_speed == 0 ? 1100 :50)
+#define MAILBOX_READ_TIMEOUT	(xpds_data[card_num].sdsl_speed == 0 ? 1100 :50)
+
+__inline__ int
+xpds_mailbox_write (int card_num, xpds_mbx_t transfer_type, u8 byte)
+{
+	if (transfer_type < XPDS_MBX_PCIMASTER_MAXTYPE) {
+		/* PCI mailbox flag */
+
+		if (mailbox == NULL || card_num >= xpds_max_cards) {
+			printk (KERN_ERR "mailbox problem:  mailbox = %p, xpds_max_cards = %d, card_num = %d\n", mailbox, xpds_max_cards, card_num);
+			return 2;
+		}
+
+		/* write data into data mailbox */
+		mailbox[card_num].bytes[XPDS_MBX_PCI_DATA] = byte;
+		xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+		/* change transfer type and set handshake bit */
+		mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] =
+			transfer_type | XPDS_MBX_PCI_HANDSHAKE_BIT;
+		xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+		/* wait until data has been received */
+		mailbox_busy_wait (card_num, MAILBOX_WRITE_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT);
+
+		/* clear handshake bit */
+		mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] &=
+			~ XPDS_MBX_PCI_HANDSHAKE_BIT;
+		xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+		/* wait until data has been received */
+		mailbox_busy_wait_not (card_num, MAILBOX_WRITE_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT);
+
+		return 0;
+	} else if (transfer_type < XPDS_MBX_NICMASTER_MAXTYPE) {
+		/* 8032 mailbox flag */
+		return 2;
+	} else {
+		return 2;
+	}
+}
+
+typedef struct {
+	u8	*bytes;
+	u32	size;
+	u32	head;
+	u32	tail;
+} queue_t;
+
+static queue_t	*debug_queue = NULL;
+static queue_t	*irq_queue = NULL;
+static queue_t	*exitcode_queue = NULL;
+
+static int
+expand_queue (queue_t *q)
+{
+	u32	new_size;
+	u8	*new_bytes;
+
+	if (q->size == 0) {
+		new_size = 100;
+	} else {
+		new_size = q->size * 2;
+	}
+	dprintk (KERN_DEBUG "expanding queue %p from %d bytes to %d bytes\n",
+		q, q->size, new_size);
+	new_bytes = kmalloc (new_size, GFP_ATOMIC);
+	if (new_bytes == NULL) {
+		printk (KERN_ERR "failed to increase size of queue to %d\n", new_size);
+		return 1;
+	}
+	if (q->size > 0) {
+		memcpy (new_bytes, q->bytes + q->head, q->size - q->head);
+		memcpy (new_bytes + q->size - q->head, q->bytes, q->head);
+	}
+	if (q->bytes != NULL) {
+		dprintk (KERN_DEBUG "freeing q->bytes (%p)\n", q->bytes);
+		kfree (q->bytes);
+	}
+	q->head = 0;
+	q->tail = q->size;
+	q->size = new_size;
+	q->bytes = new_bytes;
+	return 0;
+}
+
+__inline__ static int
+add_to_queue (queue_t *q, u8 byte)
+{
+	if (q == NULL) return 1;
+	if (q->size == 0 || (q->tail + 1) % q->size == q->head) {
+		int	rc;
+		rc = expand_queue (q);
+		if (rc) return rc;
+	}
+	dprintk (KERN_DEBUG "adding %02x in queue %p position %d\n",
+		byte, q, q->tail);
+	q->bytes[q->tail] = byte;
+	q->tail ++;
+	q->tail %= q->size;
+	return 0;
+}
+
+__inline__ static int
+get_from_queue (queue_t *q, u8 *bytep)
+{
+	if (q == NULL) return 1;
+	if (q->head == q->tail) return 1;
+	dprintk (KERN_DEBUG "getting %02x from queue %p position %d\n",
+		q->bytes[q->head], q, q->head);
+	*bytep = q->bytes[q->head];
+	q->head ++;
+	q->head %= q->size;
+	return 0;
+}
+
+__inline__ int
+xpds_mailbox_read (int card_num, xpds_mbx_t transfer_type, u8 *bytep)
+{
+	u32	val;
+
+	/* check to see if NIC is the master? */
+
+	if (transfer_type < XPDS_MBX_PCIMASTER_MAXTYPE) {
+		/* change transfer type and set handshake bit */
+		mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] =
+			transfer_type | XPDS_MBX_PCI_HANDSHAKE_BIT;
+		xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+		/* wait until data has been received */
+		mailbox_busy_wait (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT);
+
+		/* get data */
+		xpds_read_control_register (card_num, AUX_MAILBOX, &val, AUX);
+		*bytep = (val & 0xff00) >> 8;
+
+		/* clear handshake bit */
+		mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] &=
+			~ XPDS_MBX_PCI_HANDSHAKE_BIT;
+		xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+		/* wait until handshake clear is received */
+		mailbox_busy_wait_not (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT);
+
+		return 0;
+	} else if (transfer_type < XPDS_MBX_NICMASTER_MAXTYPE) {
+		queue_t		*q;
+		int		rc;
+		u32		wait_until;
+
+		/* first check the appropriate queue */
+		switch (transfer_type) {
+			case XPDS_MBX_WRITE_DEBUGPORT:
+				q = &(debug_queue[card_num]); break;
+			case XPDS_MBX_WRITE_IRQTYPE:
+				q = &(irq_queue[card_num]); break;
+			case XPDS_MBX_WRITE_EXITCODE:
+				q = &(exitcode_queue[card_num]); break;
+			default:
+				return 2;
+		}
+		rc = get_from_queue (q, bytep);
+		dprintk (KERN_DEBUG "%s: got %02x from queue (%p/%02x) (rc = %d)\n", xpds_devs[card_num].name, *bytep, q, transfer_type, rc);
+		if (rc == 0) return 0;
+
+		/* not in queue, get from NIC */
+		wait_until = jiffies + (MAILBOX_READ_TIMEOUT * 2) * HZ / 1000;
+		for (;;) {
+			u32		val_nic;
+			queue_t		*q_nic;
+			int		done = 0;
+
+			xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_nic, AUX);
+			if (val_nic & XPDS_MBX_NIC_HANDSHAKE_BIT) {
+				xpds_mbx_t	nic_transfer_type;
+				u8	byte;
+
+				nic_transfer_type = (val_nic & 0x007f0000) >> 16;
+
+				switch (nic_transfer_type) {
+					case XPDS_MBX_WRITE_DEBUGPORT:
+						q_nic = &(debug_queue[card_num]); break;
+					case XPDS_MBX_WRITE_IRQTYPE:
+						q_nic = &(irq_queue[card_num]); break;
+					case XPDS_MBX_WRITE_EXITCODE:
+						q_nic = &(exitcode_queue[card_num]); break;
+					default:
+						q_nic = &(debug_queue[card_num]); break;
+				}
+				byte = (val_nic & 0xff000000) >> 24;
+				dprintk (KERN_DEBUG "%s: adding %02x to queue (%p/%02x)\n", xpds_devs[card_num].name, byte, q_nic, nic_transfer_type);
+				add_to_queue (q_nic, byte);
+
+				mailbox[card_num].bytes[XPDS_MBX_NIC_FLAGS] = 
+					transfer_type |
+					XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED;
+				xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+				mailbox_busy_wait_not (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_NIC_HANDSHAKE_BIT);
+				mailbox[card_num].bytes[XPDS_MBX_NIC_FLAGS] &=
+					~ XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED;
+				xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX);
+
+				schedule_if_no_interrupt (card_num);
+
+				if (nic_transfer_type == transfer_type) {
+					done = 1;
+				}
+			}
+			if (done || jiffies > wait_until) break;
+		}
+		rc = get_from_queue (q, bytep);
+		dprintk (KERN_DEBUG "%s: got %02x from queue (%p/%02x) (rc = %d)\n", xpds_devs[card_num].name, *bytep, q, transfer_type, rc);
+		if (rc == 0) return 0;
+
+		return 1;
+	} else {
+		return 2;
+	}
+}
+
+int
+xpds_get_sdsl_exit_code (int card_num, u8 *bytep)
+{
+	int	rc;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_WRITE_EXITCODE, bytep);
+	return rc;
+}
+
+/*
+ * This gets the mode in the flash memory, but the speed section of
+ * the mode may be a default speed instead of the actual value if the
+ * speed section is invalid (the SDSL card will operate at the default
+ * speed in this case).
+ */
+int
+xpds_get_sdsl_mode (int card_num, u32 *val)
+{
+	int	i, rc, rval = 0;
+	u8	byte = 0;
+
+	*val = 0;
+	for (i = 0; i < 4; i ++) {
+		rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_PHYSMODE, &byte);
+		if (rc > 0) rval = 1;
+		*val |= byte << ((3 - i) * 8);
+	}
+	return rval;
+}
+
+/*
+ * This gets the actual mode in the flash memory.
+ */
+int
+xpds_get_flash_sdsl_mode (int card_num, u32 *val)
+{
+	int	i, rc, rval = 0;
+	u8	byte = 0;
+
+	*val = 0;
+	for (i = 0; i < 4; i ++) {
+		rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_PHYSMODE_IN_FLASH, &byte);
+		if (rc > 0) rval = 1;
+		*val |= byte << ((3 - i) * 8);
+	}
+	return rval;
+}
+
+int
+xpds_set_sdsl_mode (int card_num, u32 val)
+{
+	int	i, rc, rval = 0;
+	u8	byte;
+
+	for (i = 0; i < 4; i ++) {
+		byte = (val >> ((3 - i) * 8)) & 0xff;
+		rc = xpds_mailbox_write (card_num, XPDS_MBX_WRITE_PHYSMODE, byte);
+		if (rc > 0) rval = 1;
+	}
+	return rval;
+}
+
+int
+xpds_reset_sdsl (int card_num)
+{
+	u32	wait_until, val;
+
+	/* reset 8032 and Rockwell */
+	dprintk (KERN_DEBUG "%s: resetting the 8032\n", xpds_devs[card_num].name);
+	xpds_read_control_register (card_num, XPDS_MCR_TXCI, &val, MAIN);
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI, val & 0x6f, MAIN);
+	xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX);
+	xpds_write_control_register (card_num, AUX_CONTROL, val & ~0x80, AUX);
+	xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX);
+	xpds_write_control_register (card_num, AUX_CONTROL, val | 0x100, AUX);
+
+	wait_until = jiffies + 1 * HZ;
+	while (jiffies < wait_until) {
+		schedule ();
+	}
+	return 0;
+}
+
+int
+xpds_start_sdsl (int card_num)
+{
+	u32	wait_until, val;
+
+	/* unreset 8032 and Rockwell */
+	dprintk (KERN_DEBUG "%s: unresetting the 8032\n", xpds_devs[card_num].name);
+
+	xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX);
+	xpds_write_control_register (card_num, AUX_CONTROL, val & ~0x100, AUX);
+
+	xpds_read_control_register (card_num, XPDS_MCR_TXCI, &val, MAIN);
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI, val | 0x90, MAIN);
+
+	xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX);
+	xpds_write_control_register (card_num, AUX_CONTROL, val | 0x80, AUX);
+
+	wait_until = jiffies + HZ * 1;
+	while (jiffies < wait_until) {
+		schedule ();
+	}
+
+	return 0;
+}
+
+typedef struct {
+	int	size;
+	u8	*image;
+} xpds_flash_image_t;
+
+int
+xpds_install_flash_image (int card_num, xpds_flash_image_t *fd)
+{
+	int			i, rc;
+	xpds_flash_image_t	kfd;
+	u8			*image;
+	int			flashsize;
+
+	flashsize = xpds_data[card_num].is_fpga ?
+		FPGA_FLASHSIZE : ASIC_FLASHSIZE;
+
+	printk (KERN_NOTICE "%s: resetting SDSL\n", xpds_devs[card_num].name);
+	rc = xpds_reset_sdsl (card_num);
+	if (rc > 0) {
+		printk (KERN_ERR "%s: SDSL reset failed\n", xpds_devs[card_num].name);
+		return rc;
+	}
+	printk (KERN_NOTICE "%s: resetting flash\n", xpds_devs[card_num].name);
+	rc = xpds_reset_flash (card_num);
+	if (rc > 0) {
+		printk (KERN_ERR "%s: flash reset failed\n", xpds_devs[card_num].name);
+		return rc;
+	}
+
+	printk (KERN_NOTICE "%s: erasing flash\n", xpds_devs[card_num].name);
+	rc = xpds_erase_flash (card_num);
+	if (rc > 0) {
+		printk (KERN_ERR "%s: flash erase failed\n", xpds_devs[card_num].name);
+		return rc;
+	}
+
+	printk (KERN_NOTICE "%s: writing flash image\n", xpds_devs[card_num].name);
+	copy_from_user (&kfd, fd, sizeof (kfd));
+	if (kfd.size > flashsize) kfd.size = flashsize;
+	image = kmalloc (kfd.size, GFP_KERNEL);
+	if (image == NULL) return 1;
+	copy_from_user (image, kfd.image, kfd.size);
+
+	for (i = 0; i < kfd.size; i ++) {
+		rc = xpds_write_flash_byte (card_num, i, image[i]);
+		if (rc > 0) {
+			printk (KERN_ERR "%s: flash write failed, address %04x, data %02x\n", xpds_devs[card_num].name, i, image[i]);
+			kfree (image);
+			return rc;
+		}
+		schedule ();
+	}
+	printk (KERN_NOTICE "%s: verifying flash image\n", xpds_devs[card_num].name);
+	rc = xpds_verify_flash (card_num, image, 0, kfd.size);
+	if (rc > 0) {
+		printk (KERN_ERR "%s: flash verify failed\n", xpds_devs[card_num].name);
+		kfree (image);
+		return rc;
+	}
+
+	xpds_reset_sdsl (card_num);
+	xpds_start_sdsl (card_num);
+
+	kfree (image);
+	return 0;
+}
+
+int
+xpds_sdsl_loopback (int card_num)
+{
+	int	rc;
+
+	dprintk (KERN_DEBUG "%s: setting SDSL loopback mode\n", xpds_devs[card_num].name);
+	rc = xpds_mailbox_write (card_num, XPDS_MBX_WRITE_TESTMODE, 0x10);
+	return rc;
+}
+
+/*
+ * Set data like hardware version, serial number, etc.
+ * Only one exit code is generated for the whole thing, except for
+ * bytes which were unwritable (which cause additional exit codes).
+ */
+static int
+xpds_set_sdsl_serial_data (int card_num, xpds_mbx_t transfer_type, u8 *data,
+	int size)
+{
+	int			i, rc, rval = 0;
+	u8			ec;
+
+	for (i = 0; i < size; i ++) {
+		rc = xpds_mailbox_write (card_num, transfer_type, data[i]);
+		if (rc > 0) {
+			rval = 1;
+			xpds_get_sdsl_exit_code (card_num, &ec);
+		}
+	}
+	rc = xpds_get_sdsl_exit_code (card_num, &ec);
+	if (rc > 0 || ec != XPDS_SDSL_CMD_COMPLETED) rval = 1;
+	return rval;
+}
+
+int
+xpds_set_sdsl_info (int card_num)
+{
+	xpds_serial_data_t	*sdata;
+	int	rc, rval = 0;
+
+	sdata = &(xpds_data[card_num].serial_data);
+
+	rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_HWVER,
+		sdata->hardware_version, sizeof (sdata->hardware_version));
+	if (rc > 0) {
+		printk (KERN_ERR "%s: failed to write hardware version\n", xpds_devs[card_num].name);
+		rval = 1;
+	}
+
+#if REWRITE_FWVER
+	rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_FWVER,
+		sdata->firmware_version, sizeof (sdata->firmware_version));
+	if (rc > 0) {
+		printk (KERN_ERR "%s: failed to write firmware version\n", xpds_devs[card_num].name);
+		rval = 1;
+	}
+#else
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER,
+		&(sdata->firmware_version[0]));
+	if (rc) rval = 1;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER,
+		&(sdata->firmware_version[1]));
+	if (rc) rval = 1;
+
+	if (rval) {
+		printk (KERN_ERR "%s: failed to read new firmware version\n", xpds_devs[card_num].name);
+		sdata->firmware_version[0] = 0;
+		sdata->firmware_version[1] = 0;
+	}
+
+	printk (KERN_INFO "%s: new firmware version is %d.%d\n",
+		xpds_devs[card_num].name, sdata->firmware_version[0],
+		sdata->firmware_version[1]);
+#endif
+
+	rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_MFGDATE,
+		sdata->mfg_date, sizeof (sdata->mfg_date));
+	if (rc > 0) {
+		printk (KERN_ERR "%s: failed to write manufacturing date\n", xpds_devs[card_num].name);
+		rval = 1;
+	}
+
+	rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_MACADDR,
+		sdata->mac_address, sizeof (sdata->mac_address));
+	if (rc > 0) {
+		printk (KERN_ERR "%s: failed to write MAC address\n", xpds_devs[card_num].name);
+		rval = 1;
+	}
+
+	rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_SERIALNUMBER,
+		sdata->serial_number, sizeof (sdata->serial_number));
+	if (rc > 0) {
+		printk (KERN_ERR "%s: failed to write serial number\n", xpds_devs[card_num].name);
+		rval = 1;
+	}
+
+	return rval;
+}
+
+int
+xpds_sdsl_allocate (void)
+{
+	if (debug_queue == NULL) {
+		debug_queue = kmalloc (xpds_max_cards * sizeof (*debug_queue), GFP_KERNEL);
+		if (debug_queue == NULL) return -ENOMEM;
+		memset (debug_queue, 0, xpds_max_cards * sizeof (*debug_queue));
+	}
+	if (irq_queue == NULL) {
+		irq_queue = kmalloc (xpds_max_cards * sizeof (*irq_queue), GFP_KERNEL);
+		if (irq_queue == NULL) return -ENOMEM;
+		memset (irq_queue, 0, xpds_max_cards * sizeof (*irq_queue));
+	}
+	if (exitcode_queue == NULL) {
+		exitcode_queue = kmalloc (xpds_max_cards * sizeof (*exitcode_queue), GFP_KERNEL);
+		if (exitcode_queue == NULL) return -ENOMEM;
+		memset (exitcode_queue, 0, xpds_max_cards * sizeof (*exitcode_queue));
+	}
+	if (mailbox == NULL) {
+		dprintk (KERN_DEBUG "allocating mailbox (%d * %d)\n",
+			xpds_max_cards, sizeof (*mailbox));
+		mailbox = kmalloc (xpds_max_cards * sizeof (*mailbox), GFP_KERNEL);
+		if (mailbox == NULL) return -ENOMEM;
+		memset (mailbox, 0, xpds_max_cards * sizeof (*mailbox));
+	}
+	return 0;
+}
+
+int
+xpds_sdsl_get_state (int card_num, u8 *state)
+{
+	return xpds_mailbox_read (card_num, XPDS_MBX_READ_STAGE, state);
+}
+
+int
+xpds_sdsl_cleanup (void)
+{
+	int	i;
+
+	for (i = 0; i < xpds_max_cards; i ++) {
+		if (debug_queue != NULL && debug_queue[i].bytes != NULL) kfree (debug_queue[i].bytes);
+		if (irq_queue != NULL && irq_queue[i].bytes != NULL) kfree (irq_queue[i].bytes);
+		if (exitcode_queue != NULL && exitcode_queue[i].bytes != NULL) kfree (exitcode_queue[i].bytes);
+	}
+	if (debug_queue != NULL) kfree (debug_queue);
+	if (irq_queue != NULL) kfree (irq_queue);
+	if (exitcode_queue != NULL) kfree (exitcode_queue);
+	if (mailbox != NULL) kfree (mailbox);
+	return 0;
+}
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-sdsl.h linux/drivers/net/xpds/xpds-sdsl.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-sdsl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-sdsl.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999, 2000 Xpeed, Inc.
+ * xpds-sdsl.h, $Revision: 1.3 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+
+#ifndef XPDS_SDSL_H
+#define XPDS_SDSL_H 1
+
+int xpds_reset_flash (int card_num);
+int xpds_read_flash_byte (int card_num, u32 address, u8 *valuep);
+int xpds_write_flash_byte (int card_num, u32 address, u8 value);
+int xpds_verify_flash (int card_num, u8 *buffer, u8 expected_byte, int length);
+int xpds_erase_flash (int card_num);
+
+typedef enum { /* Maximum 128 types total */
+                                       /* BEGIN PCI MAILBOX */
+/* Write bootdata from the mailbox */
+	XPDS_MBX_WRITE_BOOTDATA,
+	XPDS_MBX_WRITE_MACADDR,              /* Write the mac address from the mailbox */
+	XPDS_MBX_WRITE_HWVER,                /* Write the hardware version from the mailbox */
+	XPDS_MBX_WRITE_FWVER,                /* Write the firmware version from the mailbox */
+	XPDS_MBX_WRITE_PHYSMODE,             /* Write the physical layer mode */
+	XPDS_MBX_READ_MACADDR,               /* Send the mac address to the mailbox */
+	XPDS_MBX_READ_HWVER,                 /* Send the hardware version to the mailbox */
+	XPDS_MBX_READ_FWVER,                 /* Send the firmware version to the mailbox */
+	XPDS_MBX_READ_PHYSMODE,              /* Write the physical layer mode */
+	XPDS_MBX_WRITE_TESTMODE,             /* Put the rs8973 into a test mode */
+	XPDS_MBX_CLEAR_IRQ,                  /* Clear the interrupt being generated */
+	XPDS_MBX_READ_PHYS_STATE,            /* Read the physical layer state */
+	XPDS_MBX_WRITE_MFGDATE,		 /* 4 bytes */
+	XPDS_MBX_READ_MFGDATE,
+	XPDS_MBX_READ_SERIALNUMBER,		 /* 16 bytes */
+	XPDS_MBX_WRITE_SERIALNUMBER,
+	XPDS_MBX_READ_STAGE,
+	XPDS_MBX_START_BITPUMP,
+	XPDS_MBX_START_BITPUMP_FOR_MFG,
+	XPDS_MBX_READ_PHYSMODE_IN_FLASH,
+	XPDS_MBX_READ_PHYS_QUALITY,
+	XPDS_MBX_PCIMASTER_MAXTYPE,          /* Types below this value are for the PCI mailbox only */
+				       /* BEGIN NIC MAILBOX (r/w from NIC perspective) */
+	XPDS_MBX_WRITE_DEBUGPORT = 64,            /* Get the NIC supplied terminal output from the mailbox */
+	XPDS_MBX_WRITE_IRQTYPE,              /* Get the type of irq being generated */
+	XPDS_MBX_WRITE_EXITCODE,             /* Write the completion code */        
+	XPDS_MBX_NICMASTER_MAXTYPE           /* Types below this value are for NIC mailbox only */  
+} xpds_mbx_t;
+
+/*
+ * IRQ types for value returned by using XPDS_MBX_WRITE_IRQTYPE 
+ * with xpds_mailbox_read().
+ */
+typedef enum {      /* Codes for physical layer i/o */
+	XPDS_SDSL_CMD_FAILED,            /* Physical layer command failed */
+	XPDS_SDSL_CMD_COMPLETED,         /* Physical layer command completed successfu
+	lly */
+	XPDS_SDSL_CMD_TIMEOUT,           /* Physical layer command timed out */
+	XPDS_SDSL_FLASH_ERASE_ERROR,     /* Flash could not be erased */
+	XPDS_SDSL_FLASH_ZERO_ERROR,      /* Flash could not be zeroed out */
+	XPDS_SDSL_FLASH_WRITE_ERROR,     /* Flash could not be written */
+	XPDS_SDSL_AUX_MODE_PCI,          /* PCI is the aux bus master */
+	XPDS_SDSL_AUX_MODE_NIC,          /* NIC is the aux bus master */
+	XPDS_SDSL_AUX_MODE_BOTH,         /* Both can be aux master */
+	XPDS_SDSL_RAM_ERROR,             /* RAM failed a self-test */
+	XPDS_SDSL_IRQTYPE_STATE_CHANGE,  /* Physical layer has gone into an important 
+	state */
+	XPDS_SDSL_STATE_BOOTED,          /* PL State: Booted */ 
+	XPDS_SDSL_TESTMODE_NORMAL_OPERATION,         /* Return to normal operation */
+	XPDS_SDSL_TESTMODE_EXTERNAL_ANALOG_LOOPBACK, /* Transmit then receive echo */
+	XPDS_SDSL_TESTMODE_FOUR_LEVEL_SCR,           /* Transmit a continuous stream of 4 level scrambled ones */
+	XPDS_SDSL_TESTMODE_TWO_LEVEL_SCR,            /* Transmit a continuous stream of 2 level scrambled ones */
+	XPDS_SDSL_TESTMODE_INTERNAL_ANALOG_LOOPBACK, /* Loopback bypassing pins */  
+	XPDS_SDSL_TESTMODE_FORCE_LINKUP,             /* Force link up so driver can st
+	art */                               
+	XPDS_SDSL_MAX                                /* Not a code - Used for bounds c
+hecking */
+} xpds_sdsl_codes_t;
+
+/*
+ * Physical layer states for value returned by mailbox read
+ * with XPDS_RETURN_PHYS_STATE.
+ */
+#define XPDS_SDSL_STATE_LOS                   0x01
+#define XPDS_SDSL_STATE_LOST                  0x02
+#define XPDS_SDSL_STATE_TIPRING_REVERSED      0x04
+#define XPDS_SDSL_STATE_2LEVEL_TIMER_EXPIRED  0x08
+#define XPDS_SDSL_STATE_GOOD_NOISE_MARGIN     0x10
+#define XPDS_SDSL_STATE_RESERVED              0x20
+#define XPDS_SDSL_STATE_4LEVEL                0x40
+#define XPDS_SDSL_STATE_LINKUP                0x80
+
+/* 
+ * SDSL operating mode definitions
+ */
+#define XPDS_SDSL_MODE__UNUSED		0xfff80000
+#define XPDS_SDSL_MODE__NT		0x00040000
+#define XPDS_SDSL_MODE__INVERT		0x00020000
+#define XPDS_SDSL_MODE__SWAP		0x00010000
+#define XPDS_SDSL_MODE__SPEED_MASK	0x0000ffff
+
+typedef struct {
+        int     size;
+	u8      *image;
+} xpds_flash_image_t;
+
+int xpds_mailbox_write (int card_num, xpds_mbx_t transfer_type, u8 byte);
+int xpds_mailbox_read (int card_num, xpds_mbx_t transfer_type, u8 *bytep);
+int xpds_get_sdsl_exit_code (int card_num, u8 *bytep);
+int xpds_get_sdsl_mode (int card_num, u32 *value);
+int xpds_get_flash_sdsl_mode (int card_num, u32 *value);
+int xpds_set_sdsl_mode (int card_num, u32 value);
+int xpds_reset_sdsl (int card_num);
+int xpds_start_sdsl (int card_num);
+int xpds_install_flash_image (int card_num, xpds_flash_image_t *fd);
+int xpds_sdsl_loopback (int card_num);
+int xpds_set_sdsl_info (int card_num);
+int xpds_sdsl_allocate (void);
+int xpds_sdsl_get_state (int card_num, u8 *state);
+int xpds_sdsl_cleanup (void);
+
+#define XPDS_SDSL_MAILBOX_READ_INCOMPLETE	1000
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds-softnet.h linux/drivers/net/xpds/xpds-softnet.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds-softnet.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds-softnet.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,44 @@
+#ifndef XPDS_SOFT_NET_H
+#define XPDS_SOFT_NET_H 1
+
+/*
+ * 2.4 softnet api macros taken from rrunner.c
+ */
+#if (LINUX_VERSION_CODE < 0x02030e)
+#define net_device device
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x02031b)
+#define NEW_NETINIT 1
+#endif
+
+#define HAS_SOFT_NET	0x02032b
+
+#if (LINUX_VERSION_CODE < HAS_SOFT_NET)
+/*
+ * SoftNet changes
+ */
+#define dev_kfree_skb_irq(a)    dev_kfree_skb(a)
+#define netif_wake_queue(dev)   clear_bit(0, &dev->tbusy)
+#define netif_stop_queue(dev)   set_bit(0, &dev->tbusy)
+#define net_device_stats	enet_statistics
+
+static inline void netif_start_queue(struct net_device *dev)
+{
+        dev->tbusy = 0;
+        dev->start = 1;
+}
+
+#define xpds_mark_net_bh(foo) mark_bh(foo)
+#define xpds_if_busy(dev)     dev->tbusy
+#define xpds_if_running(dev)  dev->start /* Currently unused. */
+#define xpds_if_down(dev)	    {do{dev->start = 0;}while (0);}
+#else
+#define NET_BH              0
+#define xpds_mark_net_bh(foo) {do{} while(0);}
+#define xpds_if_busy(dev)     netif_queue_stopped(dev)
+#define xpds_if_running(dev)  netif_running(dev)
+#define xpds_if_down(dev)     {do{} while(0);}
+#endif
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds.c linux/drivers/net/xpds/xpds.c
--- linux-2.2.18-pre15/drivers/net/xpds/xpds.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,4027 @@
+/*
+ * Copyright 1998, 1999, 2000 Xpeed, Inc.
+ * xpds.c, $Revision: 1.33 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifndef VERSION_STRING
+#define VERSION_STRING	"$Revision: 1.33 $"
+#endif
+
+#define LT_IND_AI	0xc
+#define NT_IND_AI	0xc
+
+#ifndef __KERNEL__
+#define __KERNEL__ 1
+#endif
+#ifndef MODULE
+#define MODULE 1
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <linux/tqueue.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#if ! defined (CONFIG_PCI)
+#error "CONFIG_PCI is not defined"
+#endif
+
+#include "xpds-softnet.h"
+#include "xpds-reg.h"
+#include "xpds-fsm.h"
+#include <linux/xpds-ioctl.h>
+#include "xpds-sdsl.h"
+#include "xpds-encap-fr.h"
+#include "xpds.h"
+
+MODULE_AUTHOR("Timothy J. Lee <linux@xpeed.com>");
+MODULE_DESCRIPTION("Xpeed 200 IDSL and 300 SDSL NIC driver (frame relay)");
+
+/*
+ * Debugging stuff.
+ */
+#define DEBUG_MAIN	1
+#define DEBUG_FSM	2
+#define DEBUG_PACKET	64
+#define DEBUG_DETAILED	128
+int	xpds_debug_level = 0 /* DEBUG_MAIN | DEBUG_FSM */;
+MODULE_PARM(xpds_debug_level, "i");
+
+#ifdef DEBUG
+#define	dprintk		if (xpds_debug_level & DEBUG_MAIN) printk
+#define	dpprintk	if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_PACKET)) == (DEBUG_MAIN | DEBUG_PACKET)) printk
+#define	ddprintk	if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) == (DEBUG_MAIN | DEBUG_DETAILED)) printk
+#else
+#define	dprintk		if (0) printk
+#define	dpprintk	if (0) printk
+#define	ddprintk	if (0) printk
+#endif
+
+#define nrprintk	if (net_ratelimit()) printk
+
+/*
+ * If we are loaded module for flashing, ignore error in detection.
+ */
+int xpds_load_for_flash = 0;
+MODULE_PARM(xpds_load_for_flash, "i");
+
+/*
+ * For loopback testing only.
+ */
+int xpds_asic_loopback = 0;
+MODULE_PARM(xpds_asic_loopback, "i");
+int xpds_external_loopback = 0;
+MODULE_PARM(xpds_external_loopback, "i");
+
+/* some motherboards have 6 PCI slots... */
+#define XPDS_DEFAULT_MAX	6
+int xpds_max_cards = XPDS_DEFAULT_MAX;
+MODULE_PARM(xpds_max_cards, "i");
+
+/* time to wait for SDSL physical layer to come up */
+#define XPDS_SDSL_TIMEOUT	300
+int xpds_sdsl_timeout = XPDS_SDSL_TIMEOUT;
+MODULE_PARM(xpds_sdsl_timeout, "i");
+
+int xpds_default_dlci = XPDS_DEFAULT_DLCI;
+MODULE_PARM(xpds_default_dlci, "i");
+
+int xpds_default_dlci_cr = XPDS_DEFAULT_DLCI_CR;
+MODULE_PARM(xpds_default_dlci_cr, "i");
+
+int xpds_default_dlci_lmi = XPDS_DLCI_LMI_LT_OR_NT;
+MODULE_PARM(xpds_default_dlci_lmi, "i");
+
+int xpds_default_bridged = 0;
+MODULE_PARM(xpds_default_bridged, "i");
+
+static char *xpds_dev_name = "dsl";
+MODULE_PARM(xpds_dev_name, "s");
+
+/*
+ * At rates below 400 Kbps, the SDSL TX DMA does not work on some
+ * computers.
+ */
+#define LOW_BIT_RATE		404
+#define TX_DMA_LOW_RATE_BUG(card_num) \
+	(xpds_data[card_num].is_sdsl && \
+	xpds_data[card_num].has_tx_dma_low_rate_bug && \
+	xpds_data[card_num].sdsl_speed < LOW_BIT_RATE)
+#define RX_DMA_LOW_RATE_BUG(card_num) \
+	(xpds_data[card_num].is_sdsl && \
+	xpds_data[card_num].has_rx_dma_low_rate_bug && \
+	xpds_data[card_num].sdsl_speed < LOW_BIT_RATE)
+
+#define PCI_VENDOR_ID_XPDS      0x14b3
+#define PCI_DEVICE_ID_XPDS_1    0x0000
+
+#define ALLOW_OLD_PCI_VENDOR_ID	0
+#define PCI_VENDOR_ID_XPDS_OLD	0xeeee
+ 
+/*
+ * Set xpds_mode by insmod.
+ */
+#define XPDS_MODE_B1		1
+#define XPDS_MODE_B2		2
+#define XPDS_MODE_D		4
+#define XPDS_MODE_DEFAULT	7
+
+int	xpds_mode = XPDS_MODE_DEFAULT;
+MODULE_PARM(xpds_mode, "i");
+
+/*
+ * Guard time for the state machines.
+ */
+#define DEFAULT_GUARD_TIME	15
+int	xpds_guard_time = DEFAULT_GUARD_TIME;
+MODULE_PARM(xpds_guard_time, "i");
+
+#define RXTX_BUFFER_SIZE        2048
+
+/*
+ * Maximum packet length that is put into a hardware register.
+ * Packets received by the hardware are dropped if they are
+ * equal to or greater in length.
+ */
+#define DEFAULT_MAX_PACKET_LENGTH	0x600
+u16	xpds_max_packet_length = DEFAULT_MAX_PACKET_LENGTH;
+MODULE_PARM(xpds_max_packet_length, "i");
+
+#define RXTX_CONTROL__SWGO		0x80000000
+#define RXTX_CONTROL__HWGO		0x40000000
+#define RXTX_CONTROL__NEXT_VALID	0x20000000
+#define RXTX_CONTROL__PACKET_TAG_OFFSET	16
+#define RXTX_CONTROL__PACKET_TAG_MASK	0xf
+#define RXTX_CONTROL__PACKET_LENGTH_MASK	0xffff
+
+#define NUM_MAIN_CONTROL_REGISTERS      16
+#define NUM_FIFO_CONTROL_REGISTERS      16
+#define NUM_DMA_CONTROL_REGISTERS       48
+#define NUM_FIFO_DATA_REGISTERS		32
+#define NUM_AUX_REGISTERS		4
+
+#define NUM_FIFO			4
+#define FIFO_SIZE			(NUM_FIFO_DATA_REGISTERS / NUM_FIFO)
+
+xpds_data_t		*xpds_data = NULL;
+static int		num_xpds_found = 0;
+
+#define XPDS_MAIN    		0
+#define XPDS_RX_FIFO		2
+#define XPDS_TX_FIFO		3
+#define XPDS_RX_DMA		4
+#define XPDS_TX_DMA  		5
+#define XPDS_RX_FIFO_DATA	6
+#define XPDS_TX_FIFO_DATA	7
+#define XPDS_AUX		8
+
+#define MAIN    	XPDS_MAIN
+#define RX_FIFO		XPDS_RX_FIFO
+#define TX_FIFO		XPDS_TX_FIFO
+#define RX_DMA		XPDS_RX_DMA
+#define TX_DMA  	XPDS_TX_DMA
+#define RX_FIFO_DATA	XPDS_RX_FIFO_DATA
+#define TX_FIFO_DATA	XPDS_TX_FIFO_DATA
+#define AUX		XPDS_AUX
+
+#define NAME_SIZE	16
+
+#define NUM_DESC	14
+
+static int xpds_init (struct net_device *dev);
+
+static char *xpds_names = NULL;
+struct net_device *xpds_devs = NULL;
+
+__inline__ static int
+xpds_read_control_register_internal (int xpds_num, int register_number,
+	u32 *value, int which, int verbose)
+{
+	register_number >>= 2;
+
+	if (verbose) dprintk (KERN_DEBUG "%s: ", xpds_devs[xpds_num].name);
+	switch (which) {
+		case MAIN:
+			if (register_number > NUM_MAIN_CONTROL_REGISTERS ||
+				xpds_data[xpds_num].main_control_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].main_control_registers[register_number];
+			if (verbose) {
+				dprintk ("main_ctl[%02x]->%08x ",
+					register_number, *value);
+			}
+			break;
+		case RX_FIFO:
+			if (register_number > NUM_FIFO_CONTROL_REGISTERS ||
+				xpds_data[xpds_num].rx_fifo_control_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].rx_fifo_control_registers[register_number];
+			if (verbose) dprintk ("rx_fifo_ctl[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case TX_FIFO:
+			if (register_number > NUM_FIFO_CONTROL_REGISTERS ||
+				xpds_data[xpds_num].tx_fifo_control_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].tx_fifo_control_registers[register_number];
+			if (verbose) dprintk ("tx_fifo_ctl[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case RX_DMA:
+			if (register_number > NUM_DMA_CONTROL_REGISTERS ||
+				xpds_data[xpds_num].rx_dma_control_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].rx_dma_control_registers[register_number];
+			if (verbose) dprintk ("rx_dma_ctl[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case TX_DMA:
+			if (register_number > NUM_DMA_CONTROL_REGISTERS ||
+				xpds_data[xpds_num].tx_dma_control_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].tx_dma_control_registers[register_number];
+			if (verbose) dprintk ("tx_dma_ctl[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case RX_FIFO_DATA:
+			if (register_number > NUM_FIFO_DATA_REGISTERS ||
+				xpds_data[xpds_num].rx_fifo_data_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].rx_fifo_data_registers[register_number];
+			if (verbose) dprintk ("rx_fifo_data[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case TX_FIFO_DATA:
+			if (register_number > NUM_FIFO_DATA_REGISTERS ||
+				xpds_data[xpds_num].tx_fifo_data_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].tx_fifo_data_registers[register_number];
+			if (verbose) dprintk ("tx_fifo_data[%02x]->%08x ",
+				register_number, *value);
+			break;
+		case AUX:
+			if (register_number > NUM_AUX_REGISTERS ||
+				xpds_data[xpds_num].aux_registers == NULL) {
+				return 4;
+			}
+			*value = xpds_data[xpds_num].aux_registers[register_number];
+			if (verbose) dprintk ("aux[%02x]->%08x ",
+				register_number, *value);
+			break;
+		default:
+			if (verbose) dprintk ("\n");
+			return 4;
+	}
+	if (verbose) dprintk ("\n");
+	return 0;
+}
+
+__inline__ int
+xpds_read_control_register (int xpds_num, int register_number,
+	u32 *value, int which)
+{
+	return xpds_read_control_register_internal (xpds_num, register_number,
+		value, which, 0);
+}
+
+__inline__ int
+xpds_read_control_register_verbose (int xpds_num, int register_number,
+	u32 *value, int which)
+{
+	return xpds_read_control_register_internal (xpds_num, register_number,
+		value, which, 1);
+}
+
+__inline__ int
+xpds_read_control_register_quiet (int xpds_num, int register_number,
+	u32 *value, int which)
+{
+	return xpds_read_control_register_internal (xpds_num, register_number,
+		value, which, 0);
+}
+
+__inline__ static int
+xpds_write_control_register_internal (int xpds_num, int register_number,
+	u32 value, int which, int verbose)
+{
+	register_number >>= 2;
+
+	if (verbose) dprintk (KERN_DEBUG "%s: ", xpds_devs[xpds_num].name);
+
+	switch (which) {
+		case MAIN:
+			if (register_number > NUM_MAIN_CONTROL_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].main_control_registers[register_number] = value;
+			if (verbose) {
+				dprintk ("%08x->main_ctl[%02x] ",
+					value, register_number);
+			}
+			break;
+		case RX_FIFO:
+			if (register_number > NUM_FIFO_CONTROL_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].rx_fifo_control_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->rx_fifo_ctl[%02x] ",
+				value, register_number);
+			break;
+		case TX_FIFO:
+			if (register_number > NUM_FIFO_CONTROL_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].tx_fifo_control_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->tx_fifo_ctl[%02x] ",
+				value, register_number);
+			break;
+		case RX_DMA:
+			if (register_number > NUM_DMA_CONTROL_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].rx_dma_control_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->rx_dma_ctl[%02x] ",
+				value, register_number);
+			break;
+		case TX_DMA:
+			if (register_number > NUM_DMA_CONTROL_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].tx_dma_control_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->tx_dma_ctl[%02x] ",
+				value, register_number);
+			break;
+		case RX_FIFO_DATA:
+			if (register_number > NUM_FIFO_DATA_REGISTERS) {
+				return 4;
+			}
+			xpds_data[xpds_num].rx_fifo_data_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->rx_fifo_data[%02x] ",
+				value, register_number);
+			break;
+		case TX_FIFO_DATA:
+			if (register_number > NUM_FIFO_DATA_REGISTERS) {
+				if (verbose) dprintk ("\n");
+				return 4;
+			}
+			xpds_data[xpds_num].tx_fifo_data_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->tx_fifo_data[%02x] ",
+				value, register_number);
+			break;
+		case AUX:
+			if (register_number > NUM_AUX_REGISTERS) {
+				if (verbose) dprintk ("\n");
+				return 4;
+			}
+			xpds_data[xpds_num].aux_registers[register_number] = value;
+			if (verbose) dprintk ("%08x->aux[%02x] ",
+				value, register_number);
+			break;
+		default:
+			if (verbose) dprintk ("\n");
+			return 4;
+	}
+	if (verbose) dprintk ("\n");
+	return 0;
+}
+
+__inline__ int
+xpds_write_control_register (int xpds_num, int register_number,
+	u32 value, int which)
+{
+	return xpds_write_control_register_internal (xpds_num, register_number,
+		value, which, 0);
+}
+
+__inline__ int
+xpds_write_control_register_verbose (int xpds_num, int register_number,
+	u32 value, int which)
+{
+	return xpds_write_control_register_internal (xpds_num, register_number,
+		value, which, 1);
+}
+
+__inline__ int
+xpds_write_control_register_quiet (int xpds_num, int register_number,
+	u32 value, int which)
+{
+	return xpds_write_control_register_internal (xpds_num, register_number,
+		value, which, 0);
+}
+
+/*
+ * Return 1 if the hardware version is >= major.minor, unless
+ * the hardware version is the uninitialized 0xff.0xff value,
+ * which is assumed to be the lowest revision.  Return 0 otherwise.
+ */
+static int
+xpds_is_hardware_version (int card_num, int major, int minor)
+{
+	if (xpds_data[card_num].serial_data.hardware_version[0] == 0xff &&
+		xpds_data[card_num].serial_data.hardware_version[1] == 0xff) {
+		return 0;
+	}
+	if (xpds_data[card_num].serial_data.hardware_version[0] > major) {
+		return 1;
+	}
+	if (xpds_data[card_num].serial_data.hardware_version[0] == major &&
+		xpds_data[card_num].serial_data.hardware_version[1] >= minor) {
+		return 1;
+	}
+	return 0;
+}
+
+static int
+xpds_init_descriptors (int card_num)
+{
+	u32	bus_addr;
+
+	dprintk (KERN_DEBUG "xpds_init_descriptors (%d)\n", card_num);
+	/*
+	 * lock?
+	 */
+
+	/*
+	 * Set maximum packet length
+	 */
+	dprintk (KERN_DEBUG "setting maximum packet length to %u (0x%x)\n",
+		xpds_max_packet_length, xpds_max_packet_length);
+	xpds_write_control_register (card_num, XPDS_MCR_PACKH,
+		(xpds_max_packet_length >> 8) & 0xff, MAIN);
+	xpds_write_control_register (card_num, XPDS_MCR_PACKL,
+		xpds_max_packet_length & 0xff, MAIN);
+
+	/*
+	 * Initialize descriptor
+	 */
+	{
+		volatile xpds_rxtx_list_t	*rx_ptr, *tx_ptr;
+		int			i;
+
+		rx_ptr = xpds_data[card_num].rx_dma_list;
+		tx_ptr = xpds_data[card_num].tx_dma_list;
+		for (i = 0; i < NUM_DESC; i ++) {
+			rx_ptr->control =
+				RXTX_CONTROL__NEXT_VALID | RXTX_CONTROL__HWGO;
+			rx_ptr = rx_ptr->next;
+			tx_ptr->control =
+				RXTX_CONTROL__NEXT_VALID;
+			tx_ptr = tx_ptr->next;
+		}
+	}
+
+	/*
+	 * Initialize pointers to receive and transmit buffers
+	 * that were allocated on module initialization (by
+	 * allocate_rxtx_buffers() ).
+	 */
+	bus_addr = virt_to_bus (xpds_data[card_num].rx_dma_list);
+	dprintk (KERN_DEBUG "%s: writing RX DMA status address %p (bus address %08x)\n", xpds_devs[card_num].name, xpds_data[card_num].rx_dma_list, bus_addr);
+	xpds_write_control_register (card_num, XPDS_DMA_STAT_ADDR,
+		bus_addr, RX_DMA);
+	bus_addr = virt_to_bus (xpds_data[card_num].tx_dma_list);
+	dprintk (KERN_DEBUG "%s: writing TX DMA status address %p (bus address %08x)\n", xpds_devs[card_num].name, xpds_data[card_num].tx_dma_list, bus_addr);
+	xpds_write_control_register (card_num, XPDS_DMA_STAT_ADDR,
+		bus_addr, TX_DMA);
+	xpds_data[card_num].current_rx_dma = xpds_data[card_num].rx_dma_list;
+	xpds_data[card_num].current_tx_dma = xpds_data[card_num].tx_dma_list;
+	xpds_data[card_num].current_hw_tx_dma = xpds_data[card_num].tx_dma_list;
+	xpds_data[card_num].current_hw_rx_dma = xpds_data[card_num].rx_dma_list;
+
+	/*
+	 * release lock?
+	 */
+
+	return 0;
+}
+
+static int
+xpds_dma_enable (int card_num)
+{
+	u32	value;
+
+	dprintk (KERN_DEBUG "xpds_dma_enable (%d)\n", card_num);
+
+	/*
+	 * Check SDSL speed.  If speed mode had been set to 0
+	 * (autonegotiation mode), then the speed needs to be
+	 * read before the DMA is enabled, in order to determine
+	 * DMA low rate bug is applicable.
+	 */
+	if (xpds_data[card_num].is_sdsl) {
+		u32	waituntil = jiffies + 90 * HZ;
+
+		do {
+			u32	sdsl_mode, speed_mode;
+			int	rc;
+
+			rc = xpds_get_sdsl_mode (card_num, &sdsl_mode);
+			dprintk ("%s: SDSL mode is %08x\n",
+				xpds_devs[card_num].name, sdsl_mode);
+			speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK;
+			dprintk ("%s: SDSL speed is %d\n",
+				xpds_devs[card_num].name, speed_mode << 3);
+			xpds_data[card_num].sdsl_speed = speed_mode << 3;
+			schedule_if_no_interrupt (card_num);
+		} while (xpds_data[card_num].sdsl_speed == 0 &&
+			jiffies < waituntil);
+		printk ("%s: SDSL speed is %d kbps.\n",
+			xpds_devs[card_num].name,
+			xpds_data[card_num].sdsl_speed);
+	}
+
+	/*
+	 * Disable all interrupts
+	 */
+	xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK,
+		XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW |
+		XPDS_DMA_MASK__LONG | XPDS_DMA_MASK__ABORT |
+		XPDS_DMA_MASK__CRC, RX_DMA);
+	xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK,
+		XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW |
+		XPDS_DMA_MASK__LONG | XPDS_DMA_MASK__ABORT |
+		XPDS_DMA_MASK__CRC, TX_DMA);
+	xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR,
+		0xff /* XPDS_MCR_MASK__RX_FIFO | XPDS_MCR_MASK__TX_FIFO */, MAIN);
+
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		0xff, MAIN);
+
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value);
+
+	/*
+	 * Enable receive and transmit DMAs.  The transmit DMA is only
+	 * enabled if we do not encounter the low bit rate bug.
+	 */
+	if (! RX_DMA_LOW_RATE_BUG (card_num) ) {
+		if (! xpds_data[card_num].has_rx_dma_burst_bug) {
+			dprintk (KERN_INFO "%s: enabling burst on RX DMA\n",
+				xpds_devs[card_num].name);
+			xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+				XPDS_DMA_CONFIG__BURST_ENABLE | XPDS_DMA_CONFIG__ENABLE,
+				RX_DMA);
+		} else {
+			xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+				XPDS_DMA_CONFIG__ENABLE, RX_DMA);
+		}
+	}
+	if (! TX_DMA_LOW_RATE_BUG (card_num) ) {
+		if (! xpds_data[card_num].has_tx_dma_burst_bug) {
+			dprintk (KERN_INFO "%s: enabling burst on TX DMA\n",
+				xpds_devs[card_num].name);
+			xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+				XPDS_DMA_CONFIG__BURST_ENABLE | XPDS_DMA_CONFIG__ENABLE,
+				TX_DMA);
+		} else {
+			xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+				XPDS_DMA_CONFIG__ENABLE, TX_DMA);
+		}
+	}
+
+	/*
+	 * Enable DMA interrupts.  The transmit DMA interrupt is only
+	 * enabled if the low bit rate bug is not encountered.
+	 */
+	if (! RX_DMA_LOW_RATE_BUG (card_num) ) {
+		xpds_write_control_register (card_num, XPDS_DMA_SET_MASK,
+			XPDS_DMA_MASK__DONE, RX_DMA);
+		xpds_read_control_register (card_num, XPDS_DMA_SET_STAT,
+			&value, RX_DMA);
+		dprintk (KERN_DEBUG "%s: RX DMA status is %02x\n",
+			xpds_devs[card_num].name, value);
+	}
+
+	if (! TX_DMA_LOW_RATE_BUG (card_num) ) {
+		xpds_write_control_register (card_num, XPDS_DMA_SET_MASK,
+			XPDS_DMA_MASK__DONE, TX_DMA);
+		xpds_read_control_register (card_num, XPDS_DMA_SET_STAT,
+			&value, TX_DMA);
+		dprintk (KERN_DEBUG "%s: TX DMA status is %02x\n",
+			xpds_devs[card_num].name, value);
+	}
+
+	if (xpds_data[card_num].is_sdsl) {
+		u32	mask;
+
+		mask = XPDS_MCR_MASK__EXT;
+		if (! TX_DMA_LOW_RATE_BUG (card_num)) {
+			mask |= XPDS_MCR_MASK__TX_DMA;
+		}
+		if (! RX_DMA_LOW_RATE_BUG (card_num)) {
+			mask |= XPDS_MCR_MASK__RX_DMA;
+		} else {
+			mask |= XPDS_MCR_MASK__RX_FIFO;
+		}
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_SET, mask, MAIN);
+	} else {
+		xpds_write_control_register (card_num, XPDS_MCR_MASK_SET,
+			XPDS_MCR_MASK__RX_DMA | XPDS_MCR_MASK__TX_DMA |
+			XPDS_MCR_MASK__RXCI, MAIN);
+	}
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+#if DEBUG
+	dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value);
+	if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO");
+	if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO");
+	if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA");
+	if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA");
+	if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR");
+	if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT");
+	if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI");
+	dprintk ("\n");
+#endif
+
+	/*
+	 * Let the RX DMA begin (write to hunt bit).
+	 * If RX DMA bug, reset and unreset the RX FIFO instead.
+	 */
+	if (RX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_data[card_num].current_rx_fifo = 0;
+		xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+			XPDS_FCR_CONFIG__RESET, RX_FIFO);
+		udelay (20);
+		xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+			0, RX_FIFO);
+	} else {
+		xpds_write_control_register (card_num, XPDS_DMA_GO,
+			XPDS_DMA_GO__HUNT, RX_DMA);
+	}
+
+	/*
+	 * Reset and unreset to enable TX FIFO mode instead of
+	 * TX DMA to work around the TX DMA bug at < 400 Kbps.
+	 */
+	if (TX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_data[card_num].current_tx_fifo = 0;
+		xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+			XPDS_FCR_CONFIG__RESET, TX_FIFO);
+		udelay (20);
+		xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+			0, TX_FIFO);
+	}
+
+	return 0;
+}
+
+static int
+xpds_dma_disable (int card_num)
+{
+	u32	value;
+
+	dprintk (KERN_DEBUG "xpds_dma_disable (%d)\n", card_num);
+
+	/*
+	 * Reset the DMAs
+	 */
+	xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+		XPDS_DMA_CONFIG__RESET, RX_DMA);
+	xpds_write_control_register (card_num, XPDS_DMA_CONFIG,
+		XPDS_DMA_CONFIG__RESET, TX_DMA);
+
+	/*
+	 * Disable the DMAs
+	 */
+	xpds_write_control_register (card_num, XPDS_DMA_CONFIG, 0x0, RX_DMA);
+	xpds_write_control_register (card_num, XPDS_DMA_CONFIG, 0x0, TX_DMA);
+	/*
+	 * Disable the DMA interrupts
+	 */
+	xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK,
+		XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW, RX_DMA);
+	xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK,
+		XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW, TX_DMA);
+	xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR,
+		XPDS_MCR_MASK__RX_DMA | XPDS_MCR_MASK__TX_DMA, MAIN);
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value);
+
+	return 0;
+}
+
+#define LED_OFF	0
+#define LED_ON	1
+/*
+ * Turn on/off the TX or activity LED
+ */
+__inline__ static void
+xpds_tx_led (int card_num, int on)
+{
+	if (xpds_data[card_num].is_fpga) {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_FPGA_TX_LED, MAIN );
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_FPGA_TX_LED, MAIN );
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_FPGA_TX_LED |
+				XPDS_MCR_GPIO__OE_FPGA_TX_LED, MAIN );
+		}
+	} else if (xpds_data[card_num].is_sdsl) {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN);
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_SDSL_ACT_LED, MAIN);
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_SDSL_ACT_LED |
+				XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN);
+		}
+	} else {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN);
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_ASIC_ACT_LED, MAIN);
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_ASIC_ACT_LED |
+				XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN);
+		}
+	}
+}
+
+/*
+ * Turn on/off the RX or activity LED
+ */
+__inline__ static void
+xpds_rx_led (int card_num, int on)
+{
+	if (xpds_data[card_num].is_fpga) {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN );
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_FPGA_RX_LED, MAIN );
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_FPGA_RX_LED |
+				XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN );
+		}
+	} else if (xpds_data[card_num].is_sdsl) {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN);
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_SDSL_ACT_LED, MAIN);
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_SDSL_ACT_LED |
+				XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN);
+		}
+	} else {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN);
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_ASIC_ACT_LED, MAIN);
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_ASIC_ACT_LED |
+				XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN);
+		}
+	}
+}
+
+
+/*
+ * Turn on the link LED
+ */
+__inline__ static void
+xpds_link_led (int card_num, int on)
+{
+	if (! xpds_data[card_num].is_fpga) {
+		if (on) {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN);
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_CLR,
+				XPDS_MCR_GPIO__GP_ASIC_LINK_LED, MAIN);
+		} else {
+			xpds_write_control_register (card_num,
+				XPDS_MCR_GPIO_SET,
+				XPDS_MCR_GPIO__GP_ASIC_LINK_LED |
+				XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN);
+		}
+	}
+}
+
+/*
+ * Resets the XPDS card, including running the PEB 2091 state machine
+ * into transparent mode by calling xpds_fsm().
+ */
+static int
+xpds_reset (int card_num)
+{
+	int	rc;
+	int	i;
+	int	speed_mode;
+	u32	mode;
+	u32	value;
+
+	dprintk (KERN_DEBUG "%s: xpds_reset\n", xpds_devs[card_num].name);
+
+	rc = xpds_dma_disable (card_num);
+	if (rc != 0) return rc;
+
+	/*
+	 * Reset the TX FIFO, then unreset it.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, TX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO);
+
+	/*
+	 * For each FIFO, zero the status.
+	 */
+	for (i = 0; i < NUM_FIFO; i ++ ) {
+		xpds_write_control_register (card_num, XPDS_FCR_STAT,
+			0, TX_FIFO);
+		xpds_write_control_register (card_num, XPDS_FCR_INC,
+			XPDS_FCR_INC__NEXT, TX_FIFO);
+	}
+
+	/*
+	 * Reset the TX FIFO, then unreset it.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, TX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO);
+
+	/*
+	 * Reset the RX FIFO, then unreset it.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, RX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO);
+
+	/*
+	 * For each FIFO, zero the status.
+	 */
+	for (i = 0; i < NUM_FIFO; i ++ ) {
+		xpds_write_control_register (card_num, XPDS_FCR_STAT,
+			0, RX_FIFO);
+		xpds_write_control_register (card_num, XPDS_FCR_INC,
+			XPDS_FCR_INC__NEXT, RX_FIFO);
+	}
+
+	/*
+	 * Reset the RX FIFO, then unreset it.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, RX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO);
+
+	/*
+	 * Enable TX and RX HDLC
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_TXCFG, 
+		XPDS_MCR_TXCFG__ENABLE, MAIN);
+	if (xpds_data[card_num].has_last_byte_bug) {
+		xpds_write_control_register (card_num, XPDS_MCR_RXCFG, 
+			(xpds_data[card_num].is_fpga ?
+				XPDS_MCR_RXCFG__ENABLE :
+				XPDS_MCR_RXCFG__ENABLE | XPDS_MCR_RXCFG__NO_CRC),
+			MAIN);
+	} else {
+		xpds_write_control_register (card_num, XPDS_MCR_RXCFG, 
+			XPDS_MCR_RXCFG__ENABLE, MAIN);
+	}
+
+	/* 
+	 * Set mode register to normal mode.
+	 */
+	if (xpds_asic_loopback) {
+		dprintk (KERN_INFO "%s: setting ASIC loopback\n", xpds_devs[card_num].name);
+		xpds_write_control_register (card_num, XPDS_MCR_CONFIG,
+			XPDS_MCR_CONFIG__MODE_LOOPBACK, MAIN);
+	} else {
+		xpds_write_control_register (card_num, XPDS_MCR_CONFIG,
+			XPDS_MCR_CONFIG__MODE_NORMAL, MAIN);
+	}
+
+	/*
+	 * Turn on activation LED
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_TEST, 1, MAIN);
+
+	/*
+	 * Clear all bits in GPIO
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_GPIO_CLR, 0xff, MAIN);
+
+	/*
+	 * Enable GPIO outputs
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET,
+		XPDS_MCR_GPIO__OE_MASK, MAIN);
+
+	/*
+	 * Enable NT or LT mode
+	 */
+	if (xpds_data[card_num].is_fpga) {
+		xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET,
+			(xpds_data[card_num].is_lt ?
+				XPDS_MCR_GPIO__GP_FPGA_LT :
+				XPDS_MCR_GPIO__GP_FPGA_NT) |
+			XPDS_MCR_GPIO__OE_MASK,
+			MAIN);
+	}
+
+	/*
+	 * Turn off the LEDs
+	 */
+	if (xpds_data[card_num].is_fpga) {
+		xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET,
+			XPDS_MCR_GPIO__GP_FPGA_TX_LED |
+			XPDS_MCR_GPIO__GP_FPGA_RX_LED |
+			XPDS_MCR_GPIO__OE_FPGA_TX_LED |
+			XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN);
+	} else {
+		xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET,
+			XPDS_MCR_GPIO__GP_ASIC_ACT_LED |
+			XPDS_MCR_GPIO__GP_ASIC_LINK_LED |
+			XPDS_MCR_GPIO__OE_ASIC_ACT_LED |
+			XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN);
+	}
+
+	/*
+	 * Enable interrupt which notifies a change in the indication
+	 * code of the PEB 2091 (RXCI) or the interrupt which indicates
+	 * a change in the status of the SDSL (EXT).
+	 */
+	if (xpds_data[card_num].is_sdsl) {
+		xpds_write_control_register (card_num, XPDS_MCR_MASK_SET,
+			XPDS_MCR_MASK__EXT, MAIN);
+	} else {
+		xpds_write_control_register (card_num, XPDS_MCR_MASK_SET,
+			XPDS_MCR_MASK__RXCI, MAIN);
+	}
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_SET,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n",
+		xpds_devs[card_num].name, value);
+
+	rc = xpds_init_descriptors (card_num);
+	if (rc != 0) return rc;
+
+	mode = 0;
+	if (xpds_data[card_num].is_sdsl) {
+		u32	sdsl_mode;
+
+		/*
+		 * Reset and unreset the SDSL, then get the SDSL mode.
+		 */
+		rc = xpds_reset_sdsl (card_num);
+		if (rc != 0) return rc;
+		rc = xpds_start_sdsl (card_num);
+		if (rc != 0) return rc;
+
+		rc = xpds_get_sdsl_mode (card_num, &sdsl_mode);
+		dprintk ("%s: SDSL mode is %08x\n", xpds_devs[card_num].name, sdsl_mode);
+		speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK;
+		dprintk ("%s: SDSL speed is %d\n", xpds_devs[card_num].name, speed_mode << 3);
+		xpds_data[card_num].sdsl_speed = speed_mode << 3;
+	} else {
+		/*
+		 * Take the PEB 2091 out of reset (B1, B2, and D channels for
+		 * 144k operation).  Only for IDSL card.
+		 */
+		speed_mode = xpds_data[card_num].speed_mode;
+		if (speed_mode & XPDS_MODE_B1) {
+			if (speed_mode & XPDS_MODE_B2) {
+				if (speed_mode & XPDS_MODE_D) {
+					dprintk ("%s: selecting B1B2D (144k)\n", xpds_devs[card_num].name);
+					mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2D;
+				} else {
+					dprintk ("%s: selecting B1B2 (128k)\n", xpds_devs[card_num].name);
+					mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2;
+				}
+			} else {
+				dprintk ("%s: selecting B1 (64k)\n", xpds_devs[card_num].name);
+				mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1;
+			}
+		} else if (speed_mode & XPDS_MODE_B2) {
+			dprintk ("%s: selecting B2 (64k)\n", xpds_devs[card_num].name);
+			mode |= XPDS_MCR_TXCI__PMD_CONFIG_B2;
+		} else {
+			dprintk (KERN_ERR "%s: invalid mode %d\n", xpds_devs[card_num].name, speed_mode);
+			dprintk ("%s: selecting B1B2D (144k)\n", xpds_devs[card_num].name);
+			mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2D;
+		}
+		xpds_write_control_register (card_num, XPDS_MCR_TXCI, mode, MAIN);
+	}
+
+	/*
+	 * Set the PMD enable bit.
+	 */
+	if (! xpds_data[card_num].is_sdsl) {
+		xpds_read_control_register (card_num, XPDS_MCR_TXCI, &mode, MAIN);
+	}
+	mode |= XPDS_MCR_TXCI__PMD_ENABLE | XPDS_MCR_TXCI__PMD_RESQ;
+	xpds_write_control_register (card_num, XPDS_MCR_TXCI, mode, MAIN);
+
+	if (xpds_data[card_num].is_sdsl) {
+		rc = xpds_mailbox_write (card_num, XPDS_MBX_START_BITPUMP, 0);
+		DELAY_HZ (3 * HZ / 2, card_num);
+		if (xpds_external_loopback) {
+			xpds_sdsl_loopback (card_num);
+			DELAY (2, card_num);
+		}
+	} else {
+		/*
+		 * need to control state machine for non-SDSL cards
+		 */
+		if (xpds_data[card_num].is_lt) {
+			rc = xpds_fsm_lt (card_num, mode, xpds_guard_time);
+		} else {
+			rc = xpds_fsm_nt (card_num, mode, xpds_guard_time);
+		}
+		if (rc != 0) return rc;
+	}
+
+	/*
+	 * Turn on the link LED
+	 */
+	if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 1);
+
+	/*
+	 * Reset the FIFOs.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, TX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG,
+		XPDS_FCR_CONFIG__RESET, RX_FIFO);
+
+	DELAY_HZ (1, card_num);
+
+	/*
+	 * For each FIFO, zero the status.
+	 */
+	for (i = 0; i < NUM_FIFO; i ++ ) {
+		xpds_write_control_register (card_num, XPDS_FCR_STAT,
+			0, TX_FIFO);
+		xpds_write_control_register (card_num, XPDS_FCR_INC,
+			XPDS_FCR_INC__NEXT, TX_FIFO);
+		xpds_write_control_register (card_num, XPDS_FCR_STAT,
+			0, RX_FIFO);
+		xpds_write_control_register (card_num, XPDS_FCR_INC,
+			XPDS_FCR_INC__NEXT, RX_FIFO);
+	}
+
+	/*
+	 * Unreset the FIFOs.
+	 */
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO);
+
+	xpds_read_control_register (card_num, XPDS_FCR_CONFIG, &value, RX_FIFO);
+	dprintk (KERN_DEBUG "%s: RX FIFO config register is %08x\n",
+		xpds_devs[card_num].name, value);
+
+	/*
+	 * For IDSL, physical layer is up.
+	 * For SDSL, wait until interrupt occurs to let us know.
+	 */
+	if (! xpds_data[card_num].is_sdsl) {
+		struct net_device	*dev;
+
+		printk ("%s: physical link came up\n", xpds_devs[card_num].name);
+		xpds_data[card_num].physical_up = 1;
+		rc = xpds_dma_enable (card_num);
+		dev = &(xpds_devs[card_num]);
+		netif_start_queue(dev);
+		if (rc != 0) return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Called from interrupt handler to receive a packet.
+ */
+static void
+xpds_rx (struct net_device *dev, int len, volatile u8 *buf)
+{
+	struct sk_buff	*skb;
+	int		card_num;
+	struct frad_local	*fp;
+	short		dlci;
+	xpds_data_t	*data_ptr;
+	int		i;
+
+	card_num = dev - xpds_devs;
+	dprintk (KERN_DEBUG "xpds_rx (%p (%d), %d, %p)\n",
+		dev, card_num, len, buf);
+	xpds_rx_led (card_num, LED_ON);
+
+	data_ptr = dev->priv;
+	fp = &(data_ptr->frad_data);
+	dlci = data_ptr->dlci;
+
+	ddprintk (KERN_DEBUG "data_ptr = %p, fp = %p\n", data_ptr, fp);
+
+#if DEBUG
+	{
+		int	i, debuglen;
+
+		debuglen = len;
+		if (debuglen > sizeof (struct frhdr)) {
+			debuglen = sizeof (struct frhdr);
+		}
+		dpprintk (KERN_DEBUG "frame header (%p) received:", buf);
+		for (i = 0; i < debuglen; i ++) {
+			dpprintk (" %02x", buf[i]);
+		}
+		dpprintk ("\n");
+	}
+	{
+		int	i, debuglen;
+
+		debuglen = len;
+		if (debuglen > 256) debuglen = 256;
+		dpprintk (KERN_DEBUG "data (%p) received:",
+			buf + sizeof (struct frhdr));
+		for (i = sizeof (struct frhdr); i < debuglen ; i ++) {
+			dpprintk (" %02x", buf[i]);
+		}
+		if (len > debuglen) dpprintk (" ...");
+		dpprintk ("\n");
+	}
+#endif
+
+	if (dlci == 0) {
+		data_ptr->stats.rx_errors ++;
+		nrprintk (KERN_ERR "xpds_rx failed -- DLCI 0???\n");
+		xpds_rx_led (card_num, LED_OFF);
+		return;
+	}
+
+	for (i = 0; i < CONFIG_DLCI_MAX; i ++) {
+		if (fp->dlci[i] == dlci) break;
+	}
+	if (i == CONFIG_DLCI_MAX) {
+		data_ptr->stats.rx_errors ++;
+		nrprintk (KERN_ERR "xpds_rx failed -- invalid DLCI %d\n", dlci);
+		xpds_rx_led (card_num, LED_OFF);
+		return;
+	}
+	ddprintk (KERN_DEBUG "dlci = %d, i = %d\n", dlci, i);
+
+	skb = dev_alloc_skb (len);
+	if (skb == NULL) {
+		printk (KERN_ERR "%s: unable to allocate skb for packet reception\n", dev->name);
+		return;
+	}
+	memcpy (skb_put (skb, len), (void *)buf, len);
+	skb->dev = dev;
+	/* skb->protocol = eth_type_trans (skb, dev); */
+	skb->ip_summed = CHECKSUM_NONE; /* software will do checksum */
+	xpds_dlci_receive (skb, dev);
+	/* netif_rx (skb); */
+
+	data_ptr->stats.rx_packets ++;
+	data_ptr->stats.rx_bytes ++;
+
+	dprintk (KERN_DEBUG "xpds_rx done\n");
+
+	dev->last_rx = jiffies;
+	xpds_rx_led (card_num, LED_OFF);
+}
+
+static struct tq_struct *xpds_reset_bh_tasks;
+
+/*
+ * A bottom half function that is meant to be put on a task queue
+ * if the link goes down (i.e. PEB 2091 falls out of transparent mode)
+ * and either a transmit is attempted or an RXCI interrupt (indicating
+ * that the other side may be back up) is received.
+ */
+static void
+xpds_reset_bh (void *p)
+{
+	int	rc;
+	int	card_num;
+	struct net_device *dev;
+
+	dev = (struct net_device *)p;
+
+	card_num = dev - xpds_devs;
+
+	dprintk (KERN_DEBUG "%s: retrying physical link / state machine, calling xpds_reset\n", xpds_devs[card_num].name);
+
+	/*
+	 * The guard times should be different on LT vs. NT
+	 * when retrying.  Should the guard times change every
+	 * retry?
+	 */
+	xpds_guard_time = xpds_data[card_num].is_lt ? 3 : 2;
+	rc = xpds_reset (card_num);
+	if (rc == 0) {
+		printk ("%s: physical link is up\n", xpds_devs[card_num].name);
+		if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 1);
+		netif_start_queue(dev);
+	}
+	xpds_data[card_num].physical_retrying = 0;
+}
+
+#if DEBUG
+static void
+xpds_print_interrupt_type (int card_num, u32 interrupt)
+{
+	u32	value;
+
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value);
+	if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO");
+	if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO");
+	if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA");
+	if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA");
+	if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR");
+	if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT");
+	if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI");
+	dprintk ("\n");
+	dprintk (KERN_DEBUG "%s: received interrupt %02x",
+		xpds_devs[card_num].name, interrupt);
+	if (interrupt & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO");
+	if (interrupt & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO");
+	if (interrupt & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA");
+	if (interrupt & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA");
+	if (interrupt & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR");
+	if (interrupt & XPDS_MCR_MASK__EXT) dprintk (" EXT");
+	if (interrupt & XPDS_MCR_MASK__RXCI) dprintk (" RXCI");
+	dprintk ("\n");
+	xpds_read_control_register (card_num, XPDS_DMA_SET_MASK,
+		&value, RX_DMA);
+	dprintk (KERN_DEBUG "%s: RX DMA mask is %02x\n", xpds_devs[card_num].name, value);
+	xpds_read_control_register (card_num, XPDS_DMA_SET_MASK,
+		&value, TX_DMA);
+	dprintk (KERN_DEBUG "%s: TX DMA mask is %02x\n", xpds_devs[card_num].name, value);
+}
+
+static void
+xpds_print_fifo_data (int card_num, u32 interrupt)
+{
+	u32	value;
+
+	if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) != (DEBUG_MAIN | DEBUG_DETAILED)) return;
+
+	if (interrupt & XPDS_MCR_INT__RX_FIFO) {
+		int	i, rc;
+		u8	*vptr;
+		const volatile xpds_rxtx_list_t	*rxtx;
+
+		ddprintk (KERN_DEBUG "RX FIFO control registers:");
+		for (i = 0; i < NUM_FIFO_CONTROL_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, RX_FIFO);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		ddprintk (KERN_DEBUG "RX FIFO data registers:");
+		for (i = 0; i < NUM_FIFO_DATA_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, RX_FIFO_DATA);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		ddprintk (KERN_DEBUG "TX FIFO control registers:");
+		for (i = 0; i < NUM_FIFO_CONTROL_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, TX_FIFO);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		ddprintk (KERN_DEBUG "TX FIFO data registers:");
+		for (i = 0; i < NUM_FIFO_DATA_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, TX_FIFO_DATA);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		ddprintk (KERN_DEBUG "RX DMA control registers:");
+		for (i = 0; i < NUM_DMA_CONTROL_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, RX_DMA);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		ddprintk (KERN_DEBUG "TX DMA control registers:");
+		for (i = 0; i < NUM_DMA_CONTROL_REGISTERS; i += 4) {
+			if (i % 16 == 0) {
+				ddprintk ("\n");
+				ddprintk (KERN_DEBUG "%08x:", i);
+			}
+			rc = xpds_read_control_register (card_num, i, &value, TX_DMA);
+			if (rc > 0) {
+				ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc);
+			} else {
+				vptr = (u8 *) &value;
+				ddprintk (" %02x %02x %02x %02x",
+					vptr[0], vptr[1], vptr[2], vptr[3]);
+			}
+		}
+		ddprintk ("\n");
+
+		for (i = 0, rxtx = xpds_data[card_num].current_rx_dma;
+			i < NUM_DESC;
+			i ++, rxtx = rxtx->next) {
+			ddprintk (KERN_DEBUG "rx[%d](%p)->control = %08x",
+				i, rxtx, rxtx->control);
+			if (rxtx->control & RXTX_CONTROL__SWGO) {
+				ddprintk (" SWGO");
+			}
+			if (rxtx->control & RXTX_CONTROL__HWGO) {
+				ddprintk (" HWGO");
+			}
+			if (rxtx->control & RXTX_CONTROL__NEXT_VALID) {
+				ddprintk (" NEXT_VALID");
+			}
+			ddprintk ("\n");
+			if (! (rxtx->control & RXTX_CONTROL__NEXT_VALID)) {
+				break;
+			}
+		}
+		for (i = 0, rxtx = xpds_data[card_num].current_tx_dma;
+			i < NUM_DESC;
+			i ++, rxtx = rxtx->next) {
+			ddprintk (KERN_DEBUG "tx[%d](%p)->control = %08x",
+				i, rxtx, rxtx->control);
+			if (rxtx->control & RXTX_CONTROL__SWGO) {
+				ddprintk (" SWGO");
+			}
+			if (rxtx->control & RXTX_CONTROL__HWGO) {
+				ddprintk (" HWGO");
+			}
+			if (rxtx->control & RXTX_CONTROL__NEXT_VALID) {
+				ddprintk (" NEXT_VALID");
+			}
+			ddprintk ("\n");
+			if (! (rxtx->control & RXTX_CONTROL__NEXT_VALID)) {
+				break;
+			}
+		}
+	}
+	xpds_read_control_register (card_num, XPDS_MCR_RXCFG, &value, MAIN);
+	ddprintk ("%s: RX HDLC config is %08x\n", xpds_devs[card_num].name, value);
+	xpds_read_control_register (card_num, XPDS_MCR_TXCFG, &value, MAIN);
+	ddprintk ("%s: TX HDLC config is %08x\n", xpds_devs[card_num].name, value);
+}
+#endif
+
+/*
+ * For SDSL cards with the TX DMA low rate bug, do the transfer from the
+ * TX DMA descriptor to the TX FIFO.  The TX FIFO interrupt indicates that
+ * the TX FIFO is ready to receive up to 32 bytes of data; this function
+ * simulates the hardware's method of copying data from the TX DMA to the
+ * TX FIFO.
+ *
+ * Note that this function keep stuffing the TX FIFO as long as it is
+ * ready (i.e. the TX FIFO interrupt bit shows 1), since if there is
+ * too long a delay for the next piece of the packet to be put in the
+ * TX FIFO, the packet will be aborted.
+ */
+#define TX_FIFO_DO_MORE	1
+#define TX_FIFO_DONE	2
+__inline__ static int
+xpds_tx_fifo_interrupt (int card_num)
+{
+	volatile u32	*tx_fifo_data_pointer;
+	volatile u8	*data;
+	int		tx_len, copy_len, i;
+	u32		control, length, offset;
+	u32		value;
+	struct timeval	tv1, tv2;
+	int		tvdiff;
+
+	/*
+	 * If there is no data in TX DMA (i.e. no more packets to
+	 * send), disallow the TX FIFO interrupt (which will be
+	 * enabled the next time a packet is transmitted by
+	 * xpds_hw_tx() ) and be done.
+	 */
+	control = xpds_data[card_num].current_hw_tx_dma->control;
+	if (! (control & RXTX_CONTROL__HWGO) ) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_CLR, XPDS_MCR_INT__TX_FIFO, MAIN);
+		return TX_FIFO_DONE;
+	}
+
+	/*
+	 * Check TX FIFO interrupt to see if hardware is
+	 * ready for the next TX FIFO to be filled.
+	 * If not, allow TX FIFO interrupts (so that when the
+	 * TX FIFO is ready, we come back here) and be done.
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		XPDS_MCR_INT__TX_FIFO, MAIN);
+	xpds_read_control_register (card_num, XPDS_MCR_INT_CLR,
+		&value, MAIN);
+	if (! (value & XPDS_MCR_INT__TX_FIFO)) {
+		dprintk (KERN_DEBUG "%s: TX FIFO interrupt not asserted\n", xpds_devs[card_num].name);
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_SET, XPDS_MCR_INT__TX_FIFO, MAIN);
+		xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+			XPDS_MCR_INT__TX_FIFO, MAIN);
+		return TX_FIFO_DONE;
+	}
+	do_gettimeofday (&tv1);
+
+	/*
+	 * Get needed information about the length, offset,
+	 * and data pointer in the TX DMA descriptor.  Note
+	 * that the length in the control field is one less
+	 * than the actual length; we add 1 here to get the
+	 * true length.
+	 */
+	length = (control & RXTX_CONTROL__PACKET_LENGTH_MASK) + 1;
+	offset = xpds_data[card_num].current_hw_tx_dma->offset;
+	data = xpds_data[card_num].current_hw_tx_dma->buffer + offset;
+
+#if DEBUG
+	xpds_read_control_register (card_num, XPDS_FCR_CONFIG,
+		&value, TX_FIFO);
+	dprintk (KERN_DEBUG "%s: TX FIFO config is %02x\n",
+		xpds_devs[card_num].name, value);
+#endif
+
+	/*
+	 * Put data in the appropriate TX FIFO.  Data must be
+	 * set / copied into the TX FIFO data registers in 32
+	 * bit chunks, not 8 bit chunks.
+	 */
+	tx_fifo_data_pointer = (volatile u32 *) (xpds_data[card_num].tx_fifo_data_registers + FIFO_SIZE * xpds_data[card_num].current_tx_fifo);
+	dprintk (KERN_DEBUG "%s: current_tx_fifo=%d, tx_fifo_data_pointer=%p\n", xpds_devs[card_num].name, xpds_data[card_num].current_tx_fifo, tx_fifo_data_pointer);
+	
+	if (length - offset > FIFO_SIZE * sizeof (u32)) {
+		tx_len = FIFO_SIZE * sizeof (u32);
+	} else {
+		tx_len = length - offset;
+	}
+
+	copy_len = tx_len % 4 == 0 ? tx_len : (tx_len + 4) / 4 * 4;
+	for (i = 0; i < copy_len / 4; i ++) {
+		tx_fifo_data_pointer[i] = *((u32 *)data + i);
+	}
+
+	dprintk (KERN_DEBUG "%s: transmitting %d bytes through FIFO (%d left)\n", xpds_devs[card_num].name, tx_len, length - offset);
+#if DEBUG
+	dprintk (KERN_DEBUG "TX FIFO data (%p):", tx_fifo_data_pointer);
+	for (i = 0; i < tx_len; i ++) {
+		dprintk (" %02x", ((volatile u8 *)tx_fifo_data_pointer)[i]);
+	}
+	dprintk ("\n");
+#endif
+
+	/*
+	 * Increment the current TX FIFO number and data pointer.
+	 * Decrement the length.
+	 */
+	xpds_data[card_num].current_tx_fifo ++;
+	xpds_data[card_num].current_tx_fifo %= NUM_FIFO;
+	offset += tx_len;
+	xpds_data[card_num].current_hw_tx_dma->offset = offset;
+
+	/*
+	 * Tell the hardware that the data is ready.  Note that
+	 * the length is reduced by 1 for the hardware. 
+	 */
+	tx_len -= 1;
+	if (length - offset <= 0) {
+		tx_len |= XPDS_FCR_STAT__LAST;
+		xpds_data[card_num].current_hw_tx_dma->control &=
+			~ RXTX_CONTROL__HWGO;
+		xpds_data[card_num].current_hw_tx_dma->control |=
+			RXTX_CONTROL__SWGO;
+		xpds_data[card_num].current_hw_tx_dma->offset = 0;
+		xpds_data[card_num].current_hw_tx_dma =
+			xpds_data[card_num].current_hw_tx_dma->next;
+	}
+
+	/*
+	 * Note that due to the bug, we cannot use the FIFO registers
+	 * until at least 10 microseconds after the FIFO ready (interrupt)
+	 * is set.
+	 */
+	do_gettimeofday (&tv2);
+	tvdiff = tv2.tv_usec - tv1.tv_usec +
+		(tv2.tv_sec - tv1.tv_sec) * 1000000;
+	if (tvdiff >= 0 && tvdiff < 10) {
+		udelay (10 - tvdiff);
+	}
+
+	dprintk (KERN_DEBUG "%s: writing %02x to FIFO stat\n",
+		xpds_devs[card_num].name, tx_len);
+	xpds_write_control_register (card_num, XPDS_FCR_STAT,
+		tx_len, TX_FIFO);
+	xpds_write_control_register (card_num, XPDS_FCR_INC,
+		XPDS_FCR_INC__NEXT, TX_FIFO);
+
+#if DEBUG
+	xpds_read_control_register (card_num, XPDS_FCR_CONFIG,
+		&value, TX_FIFO);
+	dprintk (KERN_DEBUG "%s: TX FIFO config is %02x after FCR_INC\n", xpds_devs[card_num].name, value);
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		XPDS_MCR_INT__TX_FIFO, MAIN);
+	xpds_read_control_register (card_num, XPDS_MCR_INT_CLR,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt value is %02x\n",
+		xpds_devs[card_num].name, value);
+#endif
+	return TX_FIFO_DO_MORE;
+}
+
+/*
+ * For SDSL cards with the RX DMA low rate bug, do the transfer from the
+ * RX FIFO descriptor to the RX DMA.  The RX FIFO interrupt indicates that
+ * there is data in the RX FIFO that should be copied to the RX DMA.
+ *
+ * Note that this function keep taking from the RX FIFO as long as it is
+ * ready (i.e. the RX FIFO interrupt bit shows 1), since if there is
+ * too long a delay, pieces of the packet may be lost.
+ */
+#define RX_FIFO_DO_MORE	1
+#define RX_FIFO_DONE	2
+__inline__ static int
+xpds_rx_fifo_interrupt (int card_num)
+{
+	volatile u32	*rx_fifo_data_pointer;
+	volatile u8	*data;
+	int		rx_len, rx_last, copy_len, i;
+	u32		control, length, offset;
+	u32		value;
+	struct timeval	tv1, tv2;
+	int		tvdiff;
+
+	/*
+	 * If the RX DMA buffer is not ready for the hardware,
+	 * stop.  The packet is likely to drop in this case...
+	 */
+	control = xpds_data[card_num].current_hw_rx_dma->control;
+	if (! (control & RXTX_CONTROL__HWGO) ) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_CLR, XPDS_MCR_INT__RX_FIFO, MAIN);
+		return RX_FIFO_DONE;
+	}
+
+	/*
+	 * Check RX FIFO interrupt to see if hardware hass
+	 * filled the next RX FIFO.
+	 * If not, allow RX FIFO interrupts (so that when the
+	 * RX FIFO is ready, we come back here) and be done.
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		XPDS_MCR_INT__RX_FIFO, MAIN);
+	xpds_read_control_register (card_num, XPDS_MCR_INT_CLR,
+		&value, MAIN);
+	if (! (value & XPDS_MCR_INT__RX_FIFO)) {
+		dprintk (KERN_DEBUG "%s: RX FIFO interrupt not asserted\n", xpds_devs[card_num].name);
+		/*
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_SET, XPDS_MCR_INT__RX_FIFO, MAIN);
+		xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+			XPDS_MCR_INT__RX_FIFO, MAIN);
+		*/
+		return RX_FIFO_DONE;
+	}
+
+	do_gettimeofday (&tv1);
+
+	/*
+	 * Get needed information about the length, offset,
+	 * and data pointer in the RX DMA descriptor.  Note
+	 * that the length in the control field is one less
+	 * than the actual length; we add 1 here to get the
+	 * true length.
+	 */
+	length = (control & RXTX_CONTROL__PACKET_LENGTH_MASK);
+	offset = xpds_data[card_num].current_hw_rx_dma->offset;
+	data = xpds_data[card_num].current_hw_rx_dma->buffer + offset;
+
+	dprintk (KERN_DEBUG "%s: current RX DMA buffer = %p\n",
+		xpds_devs[card_num].name,
+		xpds_data[card_num].current_hw_rx_dma->buffer);
+	dprintk (KERN_DEBUG "%s: current RX DMA length = %d\n",
+		xpds_devs[card_num].name, length);
+	dprintk (KERN_DEBUG "%s: current RX DMA offset = %d\n",
+		xpds_devs[card_num].name, offset);
+	dprintk (KERN_DEBUG "%s: current RX DMA data pointer = %p\n",
+		xpds_devs[card_num].name, data);
+
+	/*
+	 * Copy data from the RX FIFO into the DMA buffer.  Data
+	 * must be set / copied in 32 bit chunks, not 8 bit chunks.
+	 */
+	rx_fifo_data_pointer = (volatile u32 *) (xpds_data[card_num].rx_fifo_data_registers + FIFO_SIZE * xpds_data[card_num].current_rx_fifo);
+	dprintk (KERN_DEBUG "%s: current_rx_fifo=%d, rx_fifo_data_pointer=%p\n", xpds_devs[card_num].name, xpds_data[card_num].current_rx_fifo, rx_fifo_data_pointer);
+
+	/*
+	 * Note that due to the bug, we cannot read the FIFO registers
+	 * until at least 10 microseconds after the FIFO ready (interrupt)
+	 * is set.
+	 */
+	do_gettimeofday (&tv2);
+	tvdiff = tv2.tv_usec - tv1.tv_usec +
+		(tv2.tv_sec - tv1.tv_sec) * 1000000;
+	if (tvdiff >= 0 && tvdiff < 10) {
+		udelay (10 - tvdiff);
+	}
+
+	xpds_read_control_register (card_num, XPDS_FCR_STAT,
+		&value, RX_FIFO);
+	rx_len = value & XPDS_FCR_STAT__SIZE_MASK;
+	rx_len += 1;
+	rx_last = value & XPDS_FCR_STAT__LAST;
+
+	if (length + rx_len > RXTX_BUFFER_SIZE) {
+		dprintk (KERN_ERR "%s: packet too long (length = %d, rx_len = %d)\n", xpds_devs[card_num].name, length, rx_len);
+		if (rx_last) {
+			control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK;
+			control |= (length - 1);
+			control &= ~ RXTX_CONTROL__HWGO;
+			control |= RXTX_CONTROL__SWGO;
+			control &= ~ XPDS_DMA_DESC__PACKET_TAG_MASK;
+			control |= XPDS_DMA_DESC__PACKET_TAG_LONG;
+			xpds_data[card_num].current_hw_rx_dma->control = control;
+			xpds_data[card_num].current_hw_rx_dma->offset = 0;
+		}
+		xpds_data[card_num].current_rx_fifo ++;
+		xpds_data[card_num].current_rx_fifo %= NUM_FIFO;
+		xpds_write_control_register (card_num, XPDS_FCR_INC, 
+			XPDS_FCR_INC__NEXT, RX_FIFO);
+		return RX_FIFO_DO_MORE;
+	}
+	dprintk (KERN_DEBUG "%s: receiving %d bytes through FIFO (rx_last=%x)\n", xpds_devs[card_num].name, rx_len, rx_last);
+#if DEBUG
+	dprintk (KERN_DEBUG "RX FIFO data (%p):", rx_fifo_data_pointer);
+	for (i = 0; i < rx_len; i ++) {
+		dprintk (" %02x", ((volatile u8 *)rx_fifo_data_pointer)[i]);
+	}
+	dprintk ("\n");
+#endif
+
+	copy_len = (rx_len % 4 == 0) ? rx_len : (rx_len + 4) / 4 * 4;
+	dprintk (KERN_DEBUG "%s: RX DMA offset = %d, data = %p, copy_len = %d\n", xpds_devs[card_num].name, offset, data, copy_len);
+	for (i = 0; i < copy_len / 4; i ++) {
+		*((u32 *)data + i) = rx_fifo_data_pointer[i];
+	}
+
+	/*
+	 * Increment the RX FIFO number and data pointer.
+	 */
+	xpds_data[card_num].current_rx_fifo ++;
+	xpds_data[card_num].current_rx_fifo %= NUM_FIFO;
+	xpds_write_control_register (card_num, XPDS_FCR_INC, 
+		XPDS_FCR_INC__NEXT, RX_FIFO);
+
+	/*
+	 * Increment the length and offset in the RX DMA buffer.
+	 * If this is the last piece of the packet, turn on
+	 * SW_GO and turn off HW_GO in the descriptor.
+	 */
+	length += rx_len;
+	if (rx_last) {
+		u32	tag;
+		offset = 0;
+		/*
+		 * Check for tags.
+		 */
+		xpds_read_control_register (card_num, XPDS_FCR_TAG,
+			&value, RX_FIFO);
+		tag = value & XPDS_FCR_TAG__MASK;
+		if (tag) {
+			dprintk (KERN_DEBUG "%s: RX FIFO received tag %x\n",
+				xpds_devs[card_num].name, tag);
+		}
+
+		control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK;
+		control |= (length - 1);
+		control &= ~ RXTX_CONTROL__HWGO;
+		control |= RXTX_CONTROL__SWGO;
+		control &= ~ XPDS_DMA_DESC__PACKET_TAG_MASK;
+		control |= ((tag << 16) & XPDS_DMA_DESC__PACKET_TAG_MASK);
+		dprintk (KERN_DEBUG "%s: packet length is %d\n",
+			xpds_devs[card_num].name, length);
+	} else {
+		control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK;
+		control |= length;
+		offset += rx_len;
+	}
+	xpds_data[card_num].current_hw_rx_dma->control = control;
+	xpds_data[card_num].current_hw_rx_dma->offset = offset;
+
+	if (rx_last) {
+		xpds_data[card_num].current_hw_rx_dma = xpds_data[card_num].current_hw_rx_dma->next;
+		if (! (control & RXTX_CONTROL__NEXT_VALID)) {
+			dprintk (KERN_ERR "%s: RX DMA next valid is not set\n", xpds_devs[card_num].name);
+		}
+	}
+	return RX_FIFO_DO_MORE;
+}
+
+/*
+ * If the RX or TX low rate bugs exist and the
+ * RX or TX FIFOs are ready, do the FIFO manipulations.
+ */
+__inline__ static void
+xpds_fifo_interrupts (int card_num, u32 interrupt)
+{
+	int	rx_rc = RX_FIFO_DONE, tx_rc = TX_FIFO_DONE, do_more;
+
+	dprintk (KERN_DEBUG "%s: xpds_fifo_interrupts(%d,%02x) called\n",
+		xpds_devs[card_num].name, card_num, interrupt);
+	if ((interrupt & XPDS_MCR_INT__RX_FIFO) &&
+		RX_DMA_LOW_RATE_BUG (card_num)) {
+		rx_rc = xpds_rx_fifo_interrupt (card_num);
+	}
+	if ((interrupt & XPDS_MCR_INT__TX_FIFO) &&
+		TX_DMA_LOW_RATE_BUG (card_num)) {
+		tx_rc = xpds_tx_fifo_interrupt (card_num);
+	}
+	do_more = rx_rc == RX_FIFO_DO_MORE || tx_rc == TX_FIFO_DO_MORE;
+	while (do_more) {
+		int	i, rc;
+
+		do_more = 0;
+		for (i = 0; i < xpds_max_cards; i ++) {
+			if (xpds_data[i].rxtx_mem_allocated == NULL) continue;
+			if (RX_DMA_LOW_RATE_BUG (i)) {
+				rc = xpds_rx_fifo_interrupt (i);
+				if (rc == RX_FIFO_DO_MORE) do_more = 1;
+			}
+			if (TX_DMA_LOW_RATE_BUG (i)) {
+				rc = xpds_tx_fifo_interrupt (i);
+				if (rc == TX_FIFO_DO_MORE) do_more = 1;
+			}
+		}
+	}
+}
+
+/*
+ * Handle RXCI interrupt for IDSL, indicating possible physical
+ * layer state change.
+ */
+__inline__ static void
+xpds_rxci_interrupt (int card_num, struct net_device *dev)
+{
+	int		ind_ai;
+	u32		value;
+
+	/*
+	 * Get correct IND_AI to look for.
+	 */
+	ind_ai = xpds_data[card_num].is_lt ? LT_IND_AI : NT_IND_AI;
+
+	xpds_data[card_num].rxci_interrupt_received = 1;
+	xpds_read_control_register (card_num, XPDS_MCR_RXCI,
+		&value, MAIN);
+
+	/*
+	 * If network interface is up, check to see if PEB 2091
+	 * fell out of transparent mode.
+	 */
+	if (xpds_if_busy(dev) && (value & XPDS_MCR_RXCI__MASK) != ind_ai) {
+		dprintk (KERN_DEBUG "%s: RXCI status change to 0x%02x\n", xpds_devs[card_num].name, value);
+		printk (KERN_NOTICE "%s: physical link went down\n", xpds_devs[card_num].name);
+		if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 0);
+		netif_stop_queue(dev);
+		xpds_if_down(dev);
+		xpds_data[card_num].physical_up = 0;
+		xpds_data[card_num].physical_retrying = 0;
+		memset (xpds_data[card_num].frad_data.pvc_active, 0,
+			sizeof (xpds_data[card_num].frad_data.pvc_active));
+		memset (xpds_data[card_num].frad_data.pvc_new, 0,
+			sizeof (xpds_data[card_num].frad_data.pvc_new));
+	} else {
+		dprintk (KERN_DEBUG "%s: RXCI status change to 0x%02x\n", xpds_devs[card_num].name, value);
+	}
+}
+
+/*
+ * Handle external interrupt for SDSL, indicating possible physical
+ * layer state change.
+ */
+__inline__ static void
+xpds_ext_interrupt (int card_num, struct net_device *dev)
+{
+	u8	irq_type;
+	int	rc;
+
+	/* unmask external interrupt */
+	xpds_write_control_register (card_num, XPDS_MCR_MASK_SET,
+		XPDS_MCR_INT__EXT, MAIN);
+
+	/* get IRQ type */
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_WRITE_IRQTYPE,
+		&irq_type);
+	if (rc > 0) {
+		printk (KERN_ERR "%s: mailbox read (WRITE_IRQTYPE) failed (%d)\n", xpds_devs[card_num].name, rc);
+	} else if (irq_type == XPDS_SDSL_IRQTYPE_STATE_CHANGE) {
+		u8	state;
+
+		rc = xpds_mailbox_read (card_num,
+			XPDS_MBX_READ_PHYS_STATE, &state);
+		if (rc > 0) {
+			printk (KERN_ERR "%s: mailbox read (READ_PHYS_STATE) failed (%d)\n", xpds_devs[card_num].name, rc);
+		} else {
+			if (state & XPDS_SDSL_STATE_LINKUP) {
+				int	rc;
+
+				printk (KERN_NOTICE "%s: physical link came up (state=%02x)\n", xpds_devs[card_num].name, state);
+				netif_start_queue(dev);
+				if (! xpds_data[card_num].physical_up) {
+					xpds_data[card_num].physical_up = 1;
+					xpds_data[card_num].physical_retrying = 0;
+
+					/*
+					 * Need to re-enable the DMA which was
+					 * disabled when the link went down.
+					 */
+					rc = xpds_init_descriptors (card_num);
+					if (rc) printk (KERN_ERR "%s: xpds_init_descriptors(%d) failed\n", xpds_devs[card_num].name, card_num);
+					rc = xpds_dma_enable (card_num);
+					if (rc) printk (KERN_ERR "%s: xpds_dma_enable(%d) failed\n", xpds_devs[card_num].name, card_num);
+				}
+
+			} else {
+				printk (KERN_NOTICE "%s: physical link went down (state=%02x)\n", xpds_devs[card_num].name, state);
+				netif_stop_queue(dev);
+				xpds_if_down(dev);
+				if (xpds_data[card_num].physical_up) {
+					u32	sdsl_mode, speed_mode;
+					int	rc;
+
+					xpds_data[card_num].physical_up = 0;
+					xpds_data[card_num].physical_retrying = 1;
+
+					/*
+					 * DMA can get jammed up with junk
+					 * packets when link goes down and
+					 * up.  Need to disable the DMA here
+					 * to get the ASIC unstuck.
+					 */
+					rc = xpds_dma_disable (card_num);
+					if (rc) printk (KERN_ERR "%s: xpds_dma_disable(%d) failed\n", xpds_devs[card_num].name, card_num);
+
+					/*
+					 * Reset speed mode (in case it is 0,
+					 * need it set that way for mailbox
+					 * timeouts).
+					 */
+					rc = xpds_get_sdsl_mode (card_num,
+						&sdsl_mode);
+					speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK;
+					xpds_data[card_num].sdsl_speed =
+						speed_mode << 3;
+
+				}
+			}
+		}
+	}
+}
+
+#define PACKET_READ		0
+#define RX_DMA_NOT_READY	1
+
+/*
+ * Handle an RX DMA interrupt.  Return PACKET_READ if a packet was
+ * read (erroneous or otherwise), and RX_DMA_NOT_READY if SW_GO is
+ * not set in the next descriptor.
+ */
+__inline__ static int
+xpds_rx_dma_interrupt (struct net_device *dev, int card_num)
+{
+	volatile xpds_rxtx_list_t	*current_rx;
+	int		len;
+	int		packet_good;
+#if DEBUG
+	u32		value;
+#endif
+
+	current_rx = xpds_data[card_num].current_rx_dma;
+
+	dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() called\n", xpds_devs[card_num].name);
+	dprintk (KERN_DEBUG "%s: current_rx = %p\n", xpds_devs[card_num].name, current_rx);
+	dprintk (KERN_DEBUG "%s: current_rx->control = %08x\n", xpds_devs[card_num].name, current_rx->control);
+	dprintk (KERN_DEBUG "%s: current_rx->buffer = %p\n", xpds_devs[card_num].name, current_rx->buffer);
+	dprintk (KERN_DEBUG "%s: current_rx->next = %p\n", xpds_devs[card_num].name, current_rx->next);
+	dprintk (KERN_DEBUG "%s: current_rx->prev = %p\n", xpds_devs[card_num].name, current_rx->prev);
+	{
+		volatile xpds_rxtx_list_t	*rx;
+
+		for (rx = current_rx->next; rx != current_rx; rx = rx->next) {
+			ddprintk (KERN_DEBUG "rx = %p\n", rx);
+			ddprintk (KERN_DEBUG "rx->control = %08x\n", rx->control);
+			ddprintk (KERN_DEBUG "rx->buffer = %p\n", rx->buffer);
+			ddprintk (KERN_DEBUG "rx->next = %p\n", rx->next);
+			ddprintk (KERN_DEBUG "rx->prev = %p\n", rx->prev);
+		}
+	}
+
+#if DEBUG
+	xpds_read_control_register (card_num, XPDS_DMA_CLR_STAT,
+		&value, RX_DMA);
+	ddprintk (KERN_DEBUG "%s: RX DMA status is %02x\n",
+		xpds_devs[card_num].name, value);
+#endif
+
+#if DEBUG
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+	ddprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value);
+	xpds_read_control_register (card_num, XPDS_MCR_INT_CLR,
+		&value, MAIN);
+	ddprintk (KERN_DEBUG "%s: interrupt is %02x\n", xpds_devs[card_num].name, value);
+#endif
+
+	/*
+	 * Hardware should have set the software-go bit.
+	 */
+	if (! (current_rx->control & RXTX_CONTROL__SWGO) ) {
+		dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() done, not ready\n", xpds_devs[card_num].name);
+		return RX_DMA_NOT_READY;
+	}
+
+	/*
+	 * Packet is good unless proven otherwise...
+	 */
+	packet_good = 1;
+
+	/*
+	 * Check the packet tag.  If not OK, mark the packet as not good.
+	 */
+	{
+		u32		packet_tag;
+		const char	*tag_str;
+
+		packet_tag = current_rx->control & XPDS_DMA_DESC__PACKET_TAG_MASK;
+		if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_CRC) {
+			tag_str = "CRC error";
+		} else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_BOUNDARY_VIOLATION) {
+			tag_str = "boundary violation";
+		} else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_LONG) {
+			tag_str = "long frame";
+		} else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_ABORT) {
+			tag_str = "abort received";
+		} else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_OK) {
+			tag_str = "OK";
+		} else if (packet_tag & 0x80000) {
+			tag_str = "overflow/underflow";
+		} else {
+			tag_str = "unlisted error";
+		}
+		if (packet_tag != XPDS_DMA_DESC__PACKET_TAG_OK) {
+			len = current_rx->control &
+				RXTX_CONTROL__PACKET_LENGTH_MASK;
+			len ++;
+			dprintk (KERN_ERR "%s: received packet of length %d with tag %05x (%s)\n", xpds_devs[card_num].name, len, packet_tag, tag_str);
+			packet_good = 0;
+		}
+	}
+
+	/*
+	 * Get and check length of packet.
+	 */
+	len = current_rx->control & RXTX_CONTROL__PACKET_LENGTH_MASK;
+	len ++;
+	/* buggy ASIC has packet length 2 too large */
+	if (xpds_data[card_num].has_last_byte_bug) {
+		len -= 2;
+		if (len < 0) len = 0;
+	}
+	if (len > RXTX_BUFFER_SIZE) {
+		dprintk (KERN_ERR "%s: receiving packet of length %d -- too large\n", xpds_devs[card_num].name, len);
+		packet_good = 0;
+	} else if (len < 1) {
+		dprintk (KERN_ERR "%s: receiving packet of length %d -- too small\n", xpds_devs[card_num].name, len);
+		packet_good = 0;
+	} else {
+		dprintk (KERN_DEBUG "%s: receiving packet of length %d\n",
+			xpds_devs[card_num].name, len);
+	}
+
+	/*
+	 * Receive the packet (i.e. copy it to where the kernel
+	 * deals with it) if the packet is ok.
+	 */
+	if (packet_good) {
+		xpds_rx (dev, len, current_rx->buffer);
+	}
+
+	/*
+	 * Zero the packet length.
+	 */
+	ddprintk (KERN_DEBUG "%s: zeroing packet length\n", xpds_devs[card_num].name);
+	current_rx->control &= ~ (RXTX_CONTROL__PACKET_LENGTH_MASK | (RXTX_CONTROL__PACKET_TAG_MASK << RXTX_CONTROL__PACKET_TAG_OFFSET));
+
+	/*
+	 * Turn off software-go, then turn on hardware-go to let the
+	 * hardware know that it is ok to reuse the buffer.
+	 */
+	current_rx->control &= ~ RXTX_CONTROL__SWGO;
+	current_rx->control |= RXTX_CONTROL__HWGO;
+
+	/*
+	 * Advance the current to the next descriptor.
+	 */
+	xpds_data[card_num].current_rx_dma = current_rx->next;
+
+	/*
+	 * Increment the counters.
+	 */
+	if (packet_good) {
+		xpds_data[card_num].stats.rx_packets ++;
+	} else {
+		xpds_data[card_num].stats.rx_errors ++;
+	}
+
+	dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() done, packet read\n", xpds_devs[card_num].name);
+
+	return PACKET_READ;
+}
+
+typedef struct {
+	struct net_device	*dev;
+	u32		interrupt;
+} interrupt_bh_data_t;
+
+static struct tq_struct *xpds_interrupt_bh_tasks;
+
+static void xpds_interrupt_bh (void *p);
+
+/*
+ * The interrupt handler for the card.  Interrupts may happen due
+ * to receiving a packet, or due to an RXCI status change (which
+ * occurs while the state machines on the LT and NT sides are
+ * negotiating each other into transparent mode).
+ */
+static void
+xpds_interrupt (int irq __attribute__((unused)), void *dev_instance,
+	struct pt_regs *regs __attribute__((unused)))
+{
+	struct net_device	*dev;
+	int		card_num;
+	u32		interrupt, bh_interrupts;
+
+	dprintk (KERN_DEBUG "xpds_interrupt (%d, %p, %p)\n", irq, dev_instance, regs);
+
+	dev = (struct net_device *)dev_instance;
+
+	if (dev == NULL) return;
+
+	/*
+	 * Lock.
+	 */
+#ifdef __SMP__
+        if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+                dprintk (KERN_DEBUG "%s: Duplicate entry of the interrupt handler by processor %d.\n",
+                        dev->name, hard_smp_processor_id());
+                dev->interrupt = 0;
+                return;   
+        }
+#else
+/*        if (dev->interrupt) {
+                dprintk (KERN_DEBUG "%s: Re-entering the interrupt handler.\n", dev->name);
+                return;
+        }
+	dev->interrupt = 1; */
+#endif
+
+	card_num = dev - xpds_devs;
+
+	/*
+	 * Get the interrupt type.
+	 */
+	xpds_read_control_register (card_num, XPDS_MCR_INT_SET,
+		&interrupt, MAIN);
+#if DEBUG
+	xpds_print_interrupt_type (card_num, interrupt);
+#endif
+
+	if (RX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_CLR, XPDS_MCR_INT__RX_FIFO, MAIN);
+	}
+	if (TX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_CLR, XPDS_MCR_INT__TX_FIFO, MAIN);
+	}
+
+	/*
+	 * Tell the hardware to stop generating this interrupt.
+	 * For SDSL, clear the physical layer interrupt first.
+	 * For all cards, clear the interrupt in the MCR.
+	 */
+	if ((interrupt & XPDS_MCR_INT__EXT) && xpds_data[card_num].is_sdsl) {
+		int	rc;
+
+		rc = xpds_mailbox_write (card_num, XPDS_MBX_CLEAR_IRQ, 0);
+		if (rc > 0) printk (KERN_ERR "%s: mailbox write (CLEAR_IRQ, 0) failed\n", xpds_devs[card_num].name);
+
+		/* mask external interrupt */
+		xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR,
+			XPDS_MCR_INT__EXT, MAIN);
+	}
+
+	if (interrupt & XPDS_MCR_INT__RX_DMA) {
+		xpds_write_control_register (card_num, XPDS_DMA_CLR_STAT,
+			0xff /* XPDS_DMA_STAT__DONE */, RX_DMA);
+	}
+
+	/* clear the interrupt in the MCR */
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		0xff, MAIN);
+
+	/* read the interrupt in the MCR */
+	if (xpds_debug_level) {
+		u32	value;
+		xpds_read_control_register (card_num, XPDS_MCR_INT_SET,
+			&value, MAIN);
+		dprintk (KERN_DEBUG "%s: interrupt register is %02x after clearing\n", xpds_devs[card_num].name, value);
+	}
+
+	/*
+	 * If the RX or TX low rate bugs exist and the
+	 * RX or TX FIFOs are ready, do the FIFO manipulations.
+	 */
+	xpds_fifo_interrupts (card_num, interrupt);
+
+#if DEBUG
+	xpds_print_fifo_data (card_num, interrupt);
+#endif
+
+	/*
+	 * If it is a TX DMA interrupt, clear the HW_GO and SW_GO bits
+	 * in any descriptors where both are set, and clear the interrupt.
+	 */
+	if (interrupt & XPDS_MCR_INT__TX_DMA) {
+		volatile xpds_rxtx_list_t	*tx_desc;
+		int				i;
+
+		tx_desc = xpds_data[card_num].current_tx_dma;
+		for (i = 0; i < NUM_DESC; i ++) {
+			if ((tx_desc->control & (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO)) == (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO)) {
+				tx_desc->control &= ~ (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO);
+			}
+			tx_desc = tx_desc->next;
+		}
+
+		ddprintk (KERN_DEBUG "%s: clearing TX DMA interrupt\n", xpds_devs[card_num].name);
+		xpds_write_control_register (card_num,
+			XPDS_DMA_CLR_STAT,
+			0xff /* XPDS_DMA_STAT__DONE */, TX_DMA);
+		xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+			XPDS_MCR_INT__TX_DMA, MAIN);
+
+		xpds_tx_led (card_num, LED_OFF);
+	}
+
+	/*
+	 * Check to see if the hardware has caught up with the
+	 * software.  If it has the software-go bit set, then
+	 * reset and start the DMA again.
+	 */
+	if (xpds_data[card_num].current_rx_dma->prev->control & RXTX_CONTROL__SWGO) {
+		if (! RX_DMA_LOW_RATE_BUG (card_num)) {
+			printk (KERN_INFO "%s: RX DMA overrun -- restarting RX DMA\n", xpds_devs[card_num].name);
+			xpds_dma_disable (card_num);
+			xpds_init_descriptors (card_num);
+			xpds_dma_enable (card_num);
+		}
+	}
+
+	mark_bh (NET_BH);
+
+	bh_interrupts = XPDS_MCR_INT__EXT | XPDS_MCR_INT__RXCI | XPDS_MCR_INT__RX_DMA;
+	if (RX_DMA_LOW_RATE_BUG (card_num) || TX_DMA_LOW_RATE_BUG (card_num)) {
+		bh_interrupts |= XPDS_MCR_INT__RX_FIFO | XPDS_MCR_INT__TX_FIFO;
+	}
+
+	if (interrupt & bh_interrupts) {
+		dprintk (KERN_DEBUG "queuing a bottom half task (dev = %p (%d), interrupt = %02x)\n", dev, card_num, interrupt);
+		((interrupt_bh_data_t *)(xpds_interrupt_bh_tasks[card_num].data))->dev = dev;
+		((interrupt_bh_data_t *)(xpds_interrupt_bh_tasks[card_num].data))->interrupt = interrupt;
+		queue_task (&(xpds_interrupt_bh_tasks[card_num]), &tq_immediate);
+		mark_bh (IMMEDIATE_BH);
+	}
+#if defined(__i386__)
+/*        clear_bit(0, (void*)&dev->interrupt); */
+#else
+/*	dev->interrupt = 0; */
+#endif
+}
+
+static void
+xpds_interrupt_bh (void *p)
+{
+	interrupt_bh_data_t	*data;
+	struct net_device	*dev;
+	int			card_num;
+	u32			interrupt;
+
+	dprintk (KERN_DEBUG "xpds_interrupt_bh(%p) started\n", p);
+
+	data = p;
+	dev = data->dev;
+	card_num = dev - xpds_devs;
+	interrupt = data->interrupt;
+
+	dprintk (KERN_DEBUG "card_num = %d, interrupt = %02x\n",
+		card_num, interrupt);
+
+	/*
+	 * If it is an RX DMA interrupt, need to get the packet
+	 * from the buffer that hardware wrote into.  Keep doing
+	 * this until there are no more descriptors to read,
+	 * because there may be multiple descriptors filled
+	 * during one interrupt.
+	 *
+	 * Note that if we have the TX DMA low bit rate bug, we
+	 * need to check see if we can push more data into the TX
+	 * FIFO each time we receive a packet; if this isn't done,
+	 * the TX FIFOs may get starved.
+	 */
+	if ((interrupt & XPDS_MCR_INT__RX_DMA) ||
+		((interrupt & XPDS_MCR_INT__RX_FIFO) &&
+			RX_DMA_LOW_RATE_BUG (card_num)) ) {
+		int	rc;
+		do {
+			xpds_fifo_interrupts (card_num, interrupt);
+			rc = xpds_rx_dma_interrupt (dev, card_num);
+		} while (rc == PACKET_READ);
+	}
+
+	/*
+	 * Turn on RX FIFO interrupts if necessary.
+	 */
+	if (RX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_SET, XPDS_MCR_INT__RX_FIFO, MAIN);
+	}
+
+	/*
+	 * If it is an RXCI interrupt, clear it, handle it, and go on.
+	 * The RXCI interrupt is only used for IDSL cards.
+	 */
+	if ((interrupt & XPDS_MCR_INT__RXCI) && ! xpds_data[card_num].is_sdsl) {
+		xpds_rxci_interrupt (card_num, dev);
+	}
+
+	/*
+	 * If it is an external interrupt, clear it, handle it, and go on.
+	 * The external interrupt is only used for SDSL cards.
+	 */
+	if ((interrupt & XPDS_MCR_INT__EXT) && xpds_data[card_num].is_sdsl) {
+		xpds_ext_interrupt (card_num, dev);
+	}
+
+	dprintk (KERN_DEBUG "xpds_interrupt_bh() finished\n");
+}
+
+
+/*
+ * Called when "ifconfig xpds0 up" or some such is done.
+ * Tries to bring up and reset the device.  Needs to have
+ * the other side also trying to get to transparent mode.
+ */
+static int
+xpds_open (struct net_device *dev)
+{
+	int	card_num, rc, flags;
+	u32	value;
+
+	dprintk (KERN_DEBUG "xpds_open (%p (%d))\n", dev, dev - xpds_devs);
+
+	card_num = dev - xpds_devs;
+
+	/*
+	 * Disable all interrupts.
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR,
+		0xff, MAIN);
+	xpds_write_control_register (card_num, XPDS_MCR_INT_CLR,
+		0xff, MAIN);
+	xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+		&value, MAIN);
+	dprintk (KERN_DEBUG "%s: interrupt mask is %08x\n", xpds_devs[card_num].name, value);
+
+	/*
+	 * Request the IRQ.
+	 */
+	dev->irq = xpds_data[card_num].pci_dev->irq;
+
+	flags = SA_SHIRQ;
+
+	dprintk (KERN_DEBUG "%s: request_irq (%d, %p, %08x, %s, %p)\n",
+		dev->name, dev->irq, xpds_interrupt, flags, dev->name, dev);
+	/* DELAY (2, card_num); */
+	rc = request_irq (dev->irq, &xpds_interrupt, flags, dev->name, dev);
+	if (rc != 0) {
+		dprintk (KERN_DEBUG "%s: unable to get IRQ %d (rc=%d)\n",
+			dev->name, dev->irq, rc);
+		free_irq (dev->irq, dev);
+		return -EAGAIN;
+	}
+
+	dprintk (KERN_DEBUG "%s: %s got interrupt %d\n", dev->name, dev->name, dev->irq);
+
+	/* dev->dev_addr = ??? ; */
+
+	rc = xpds_reset (card_num);
+
+	xpds_dlci_install_lmi_timer (0, dev);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * Called when "ifconfig xpds0 down" or some such is done.
+ * Brings down the device.
+ */
+static int
+xpds_stop (struct net_device *dev)
+{
+	int	card_num;
+	int	rc;
+	u32	value;
+
+	card_num = dev - xpds_devs;
+
+	dprintk (KERN_DEBUG "%s: xpds_stop(%p)\n", xpds_devs[card_num].name, dev);
+	printk (KERN_DEBUG "%s: going down\n", xpds_devs[card_num].name);
+
+	xpds_dlci_remove_lmi_timer (0, dev);
+
+	rc = xpds_dma_disable (card_num);
+	if (rc != 0) return rc;
+
+	/*
+	 * Turn off link LED, or put the SDSL in reset.
+	 */
+	if (! xpds_data[card_num].is_sdsl) {
+		xpds_link_led (card_num, 0);
+	} else {
+		xpds_reset_sdsl (card_num);
+	}
+
+	/*
+	 * Bring physical layer down.
+	 */
+	if (xpds_data[card_num].is_sdsl) {
+		xpds_reset_sdsl (card_num);
+	} else {
+		xpds_read_control_register (card_num, XPDS_MCR_TXCI, &value,
+			MAIN);
+		value &= (XPDS_MCR_TXCI__MASK | XPDS_MCR_TXCI__PMD_CONFIG_B1B2D);
+		xpds_write_control_register (card_num, XPDS_MCR_TXCI, value,
+			MAIN);
+	}
+	xpds_data[card_num].physical_up = 0;
+	xpds_data[card_num].physical_retrying = 0;
+
+	/*
+	 * Disable interrupts
+	 */
+	xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, 0xff, MAIN);
+
+	/*
+	 * Mark the device as off and busy.
+	 */
+	netif_stop_queue(dev);
+	xpds_if_down(dev);
+
+	MOD_DEC_USE_COUNT;
+
+	free_irq (dev->irq, dev);
+
+	return 0;
+}
+
+/*
+ * Does the hardware control part of the packet transmission.
+ */
+static int
+xpds_hw_tx (char *data, int len, struct net_device *dev)
+{
+	int	card_num;
+	volatile xpds_rxtx_list_t    *current_tx;
+	u32	control;
+
+	card_num = dev - xpds_devs;
+#if DEBUG
+	{
+		u32	value;
+
+		xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR,
+			&value, MAIN);
+		dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value);
+		if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO");
+		if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO");
+		if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA");
+		if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA");
+		if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR");
+		if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT");
+		if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI");
+		dprintk ("\n");
+	}
+#endif
+
+	dprintk (KERN_DEBUG "%s: transmitting packet of length %d\n",
+		xpds_devs[card_num].name, len);
+
+	/*
+	 * Make sure data is not too big.  We are assuming that the
+	 * other end has the same maximum packet length limitation.
+	 * Is this necessarily correct?
+	 */
+	if (len > xpds_max_packet_length) {
+		xpds_data[card_num].stats.tx_errors ++;
+		printk (KERN_ERR "packet size %d is too large (maximum %d)\n",
+			len, xpds_max_packet_length);
+		return -E2BIG;
+	}
+
+	/*
+	 * Get control information for the descriptor.
+	 */
+	control = xpds_data[card_num].current_tx_dma->control;
+
+	/*
+	 * Check current descriptor to see if it is available.
+	 * If software-go flag is not set, then the buffer is still
+	 * waiting for the hardware to empty it.
+	 */
+	if (! (control & RXTX_CONTROL__SWGO) && (control & RXTX_CONTROL__HWGO) ) {
+		dprintk (KERN_DEBUG "%s: unable to transmit (SWGO == 0 && HWGO == 1)\n", xpds_devs[card_num].name);
+		/* xpds_data[card_num].stats.tx_errors ++; */
+		return -EBUSY;
+	}
+
+	/*
+	 * Indicate the time a transmission started.
+	 */
+	dev->trans_start = jiffies;
+
+	/*
+	 * Fill in current transmit buffer.
+	 */
+	ddprintk (KERN_DEBUG "%s: filling current transmit buffer\n", xpds_devs[card_num].name);
+	memcpy ((void *)(xpds_data[card_num].current_tx_dma->buffer), data, len);
+
+	/*
+	 * Set up the length in the current descriptor.
+	 */
+	ddprintk (KERN_DEBUG "%s: setting length in descriptor\n", xpds_devs[card_num].name);
+	control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK;
+	control |= ((len - 1) & RXTX_CONTROL__PACKET_LENGTH_MASK);
+
+	/*
+	 * Clear the software-go flag and set the hardware-go flag
+	 * in the current descriptor.
+	 */
+	ddprintk (KERN_DEBUG "%s: clearing SWGO and setting HWGO\n", xpds_devs[card_num].name);
+	control &= ~ RXTX_CONTROL__SWGO;
+	control |= RXTX_CONTROL__HWGO;
+
+	/*
+	 * Put changed control back in the TX descriptor.
+	 */
+	xpds_data[card_num].current_tx_dma->control = control;
+
+	/*
+	 * Print out the current TX descriptor.
+	 */
+	current_tx =  xpds_data[card_num].current_tx_dma;
+
+	/*
+	 * Write a 1 to the hunt bit of the DMA go register for
+	 * the transmit DMA to cause it to fetch the next descriptor.
+	 */
+	ddprintk (KERN_DEBUG "%s: setting hunt bit\n", xpds_devs[card_num].name);
+	xpds_write_control_register (card_num, XPDS_DMA_GO,
+		XPDS_DMA_GO__HUNT, TX_DMA);
+
+	/*
+	 * Set current transmit buffer to the next one in the
+	 * circular list, after setting the offset to 0 in case
+	 * of the TX DMA bug.
+	 */
+	xpds_data[card_num].current_tx_dma->offset = 0;
+	ddprintk (KERN_DEBUG "%s: setting current transmit buffer to next\n", xpds_devs[card_num].name);
+	xpds_data[card_num].current_tx_dma =
+		xpds_data[card_num].current_tx_dma->next;
+
+	/*
+	 * Increment the counters.
+	 */
+	xpds_data[card_num].stats.tx_packets ++;
+	xpds_data[card_num].stats.tx_bytes += len;
+
+	dprintk (KERN_DEBUG "%s: transmitted packet %ld\n",
+		xpds_devs[card_num].name, (long) (xpds_data[card_num].stats.tx_packets));
+
+	/*
+	 * If we have the TX DMA bug we need to enable the TX FIFO
+	 * interrupt so that when the TX FIFO is ready, we can simulate
+	 * the hardware DMA->FIFO transfer in the driver.
+	 */
+	if (TX_DMA_LOW_RATE_BUG (card_num)) {
+		xpds_write_control_register (card_num,
+			XPDS_MCR_MASK_SET, XPDS_MCR_INT__TX_FIFO, MAIN);
+	}
+
+	dprintk (KERN_DEBUG "%s: xpds_hw_tx () done\n", xpds_devs[card_num].name);
+
+	return 0;
+}
+
+/*
+ * Called whenever a packet needs to be transmitted.
+ * Note that if the physical link is down (not in transparent
+ * mode), a bottom half to reset the device is placed on the
+ * scheduler queue.
+ */
+int
+xpds_tx (u8 *data, unsigned int len, struct net_device *dev)
+{
+	int	card_num;
+	int	rc;
+	u32	control;
+
+	card_num = dev - xpds_devs;
+
+	xpds_tx_led (card_num, LED_ON);
+
+	dprintk (KERN_DEBUG "%s: xpds_tx (%p, %u, %p)\n", xpds_devs[card_num].name,
+		data, len, dev);
+
+#if DEBUG
+	{
+		int	i, debuglen;
+
+		debuglen = len;
+		if (debuglen > sizeof (struct frhdr)) {
+			debuglen = sizeof (struct frhdr);
+		}
+		dpprintk (KERN_DEBUG "frame header sent:");
+		for (i = 0; i < sizeof (struct frhdr); i ++) {
+			dpprintk (" %02x", data[i]);
+		}
+		dpprintk ("\n");
+	}
+	{
+		int	i, debuglen;
+
+		/* debuglen = ETH_ZLEN < len ? len : ETH_ZLEN; */
+		debuglen = len;
+		if (debuglen > 256) debuglen = 256;
+		dpprintk (KERN_DEBUG "data sent:");
+		for (i = sizeof (struct frhdr); i < debuglen ; i ++) {
+			dpprintk (" %02x", data[i]);
+		}
+		if (len > debuglen) dpprintk (" ...");
+		dpprintk ("\n");
+	}
+#endif
+	if (! xpds_data[card_num].physical_up) {
+		dprintk (KERN_DEBUG "%s: physical link is down\n", xpds_devs[card_num].name);
+		if (xpds_data[card_num].is_sdsl) {
+			return -EBUSY;
+		}
+		if (! xpds_data[card_num].physical_retrying) {
+			xpds_data[card_num].physical_retrying = 1;
+
+			xpds_reset_bh_tasks[card_num].next = NULL;
+			xpds_reset_bh_tasks[card_num].sync = 0;
+			xpds_reset_bh_tasks[card_num].routine = xpds_reset_bh;
+			xpds_reset_bh_tasks[card_num].data = dev;
+
+			queue_task (&(xpds_reset_bh_tasks[card_num]),
+				&tq_scheduler);
+		}
+		xpds_tx_led (card_num, LED_OFF);
+		return -EBUSY;
+	}
+
+	/*
+	 * Atomically test and set (bit 0 of) the busy flag.
+	 * If busy, return BUSY.
+	 */
+	if (xpds_if_busy(dev)) {
+		dprintk (KERN_DEBUG "%s: busy\n", xpds_devs[card_num].name);
+		xpds_tx_led (card_num, LED_OFF);
+		/* xpds_data[card_num].stats.tx_errors ++; */
+
+		return -EBUSY;
+	}
+	netif_stop_queue(dev);
+
+	/* len = ETH_ZLEN < len ? len : ETH_ZLEN; */
+	dev->trans_start = jiffies;
+
+	rc = xpds_hw_tx (data, len, dev);
+
+	if (rc == 0) {
+		/* dev_kfree_skb (skb, FREE_WRITE); */
+	} else {
+		xpds_tx_led (card_num, LED_OFF);
+	}
+
+	/*
+	 * Get control information for the descriptor (after
+	 * the one that we just wrote into).
+	 */
+	control = xpds_data[card_num].current_tx_dma->control;
+
+	/*
+	 * Atomically clear the busy bit.
+	 */
+/*	if (test_and_clear_bit (0, (void *)&(dev->tbusy)) == 0) {
+		printk (KERN_ERR "%s: test_and_clear_bit(0, &(dev->tbusy)) in xpds_tx() found bit already clear\n", xpds_devs[card_num].name);
+	} */
+	netif_wake_queue(dev);
+
+	/*
+	 * Return OK if xpds_tx returned 0 (OK), BUSY if -EBUSY,
+	 * ERR otherwise.
+	 */
+	return rc;
+}
+
+struct net_device_stats *
+xpds_get_stats (struct net_device *dev)
+{
+	xpds_data_t	*data_ptr;
+
+	data_ptr = dev->priv;
+	return &(data_ptr->stats);
+}
+
+static int
+xpds_set_config (struct net_device *dev,
+	struct ifmap *map __attribute__ ((unused)) )
+{
+	dprintk (KERN_DEBUG "%s: xpds_set_config() called\n",
+		dev->name);
+
+	if (dev->flags & IFF_UP) return -EBUSY;
+
+	/* nothing */
+
+	return 0;
+}
+
+static void
+xpds_reboot_sdsl_physical_layer (int card_num)
+{
+	if (xpds_data[card_num].physical_up ||
+		xpds_data[card_num].physical_retrying) {
+		xpds_reset_sdsl (card_num);
+		xpds_start_sdsl (card_num);
+	}
+}
+
+static int xpds_init_frad_data (struct frad_local *fp, short dlci);
+
+static int
+xpds_ioctl (struct net_device *dev, struct ifreq *rq,
+	int cmd)
+{
+	xpds_data_t	*data_ptr;
+	int		val, card_num, rc = 0;
+
+	if (! suser ()) return -EACCES;
+
+	data_ptr = dev->priv;
+	card_num = data_ptr - xpds_data;
+	val = (int)(rq->ifr_data);
+	if (cmd == XPDS_IOCTL_SET_DEBUG) {
+#if DEBUG
+		int	i;
+
+		printk ("XPDS driver debug level set to %d\n", val);
+		xpds_debug_level = val;
+		for (i = 0; i < xpds_max_cards; i ++) {
+			xpds_devs[i].flags &= ~ IFF_DEBUG;
+			xpds_devs[i].flags |= val ? IFF_DEBUG : 0;
+		}
+#else
+		printk (KERN_ERR "This version of the XPDS driver does not have debug capability.\n");
+#endif
+	} else if (cmd == XPDS_IOCTL_SET_LT) {
+		printk ("%s: set to %s\n",
+			xpds_devs[card_num].name,
+			val ? "LT (line termination)" :
+				"NT (network termination)");
+		val = (val != 0);
+		data_ptr->is_lt = val;
+		data_ptr->frad_data.no_initiate_lmi = val;
+		if (data_ptr->is_sdsl) {
+			u32	sdsl_mode;
+			u8	byte;
+
+			xpds_get_flash_sdsl_mode (card_num, &sdsl_mode);
+			printk (KERN_DEBUG "%s: sdsl_mode = %08x\n",
+				xpds_devs[card_num].name, sdsl_mode);
+			sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED;
+			sdsl_mode &= ~ XPDS_SDSL_MODE__NT;
+			sdsl_mode |= data_ptr->is_lt ? 0 : XPDS_SDSL_MODE__NT;
+			printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			rc = xpds_set_sdsl_mode (card_num, sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			rc = xpds_get_sdsl_exit_code (card_num, &byte);
+			if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) {
+				printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte);
+				rc = -EBUSY;
+			}
+		}
+	} else if (cmd == XPDS_IOCTL_SET_IDSL_MODE) {
+		if (! data_ptr->is_sdsl) {
+			printk ("%s: set to mode %d\n", xpds_devs[card_num].name, val);
+			data_ptr->speed_mode = val;
+		} else {
+			printk (KERN_ERR "Setting the IDSL mode is only applicable to IDSL.\n");
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_SET_SDSL_SPEED) {
+		if (data_ptr->is_sdsl) {
+			u32	sdsl_mode;
+			u8	byte;
+
+			printk ("%s: SDSL speed set to %d\n",
+				xpds_devs[card_num].name,
+				((val >> 3) & XPDS_SDSL_MODE__SPEED_MASK) << 3);
+			rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED;
+			sdsl_mode &= ~ XPDS_SDSL_MODE__SPEED_MASK;
+			sdsl_mode |= (val >> 3) & XPDS_SDSL_MODE__SPEED_MASK;
+			printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			rc = xpds_set_sdsl_mode (card_num, sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			rc = xpds_get_sdsl_exit_code (card_num, &byte);
+			if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) {
+				printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte);
+				rc = -EBUSY;
+			}
+
+			xpds_reboot_sdsl_physical_layer (card_num);
+
+			if ((xpds_data[card_num].has_tx_dma_low_rate_bug ||
+				xpds_data[card_num].has_rx_dma_low_rate_bug) &&
+				xpds_data[card_num].physical_up) {
+				if ((xpds_data[card_num].sdsl_speed < LOW_BIT_RATE && val >= LOW_BIT_RATE) || (xpds_data[card_num].sdsl_speed >= LOW_BIT_RATE && val < LOW_BIT_RATE)) {
+					/*
+					 * If card has the low bit rate bug,
+					 * and the speed change moved across
+					 * the value that triggers the bug,
+					 * need to reset the DMA / FIFO.
+					 */
+					xpds_dma_disable (card_num);
+					xpds_dma_enable (card_num);
+				}
+			}
+			xpds_data[card_num].sdsl_speed = val;
+		} else {
+			printk (KERN_ERR "Setting the SDSL speed is only applicable to SDSL.\n");
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_SET_SDSL_INVERT) {
+		if (data_ptr->is_sdsl) {
+			u32	sdsl_mode;
+			u8	byte;
+
+			printk ("%s: SDSL invert set to %d\n",
+				xpds_devs[card_num].name, val ? 1 : 0);
+			rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED;
+			sdsl_mode &= ~ XPDS_SDSL_MODE__INVERT;
+			sdsl_mode |= (val ? XPDS_SDSL_MODE__INVERT : 0);
+			printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			xpds_set_sdsl_mode (card_num, sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			rc = xpds_get_sdsl_exit_code (card_num, &byte);
+			if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) {
+				printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte);
+				rc = -EBUSY;
+			}
+			xpds_reboot_sdsl_physical_layer (card_num);
+		} else {
+			printk (KERN_ERR "Setting the invert is only applicable to SDSL.\n");
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_SET_SDSL_SWAP) {
+		if (data_ptr->is_sdsl) {
+			u32	sdsl_mode;
+			u8	byte;
+
+			printk ("%s: SDSL swap set to %d\n",
+				xpds_devs[card_num].name, val ? 1 : 0);
+			rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			printk (KERN_DEBUG "%s: sdsl_mode = %08x\n",
+				xpds_devs[card_num].name, sdsl_mode);
+			sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED;
+			sdsl_mode &= ~ XPDS_SDSL_MODE__SWAP;
+			sdsl_mode |= (val ? XPDS_SDSL_MODE__SWAP : 0);
+			printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode);
+			rc = xpds_set_sdsl_mode (card_num, sdsl_mode);
+			if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name);
+			rc = xpds_get_sdsl_exit_code (card_num, &byte);
+			if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) {
+				printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte);
+				rc = -EBUSY;
+			}
+			xpds_reboot_sdsl_physical_layer (card_num);
+		} else {
+			printk (KERN_ERR "Setting the swap is only applicable to SDSL.\n");
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_INSTALL_FLASH) {
+		rc = xpds_install_flash_image (card_num,
+			(xpds_flash_image_t *)(rq->ifr_data));
+		if (rc > 0) rc = -EBUSY;
+	} else if (cmd == XPDS_IOCTL_GET_SDSL_INFO) {
+		if (data_ptr->is_sdsl) {
+			printk ("%s: getting SDSL serial info for user process\n", xpds_devs[card_num].name);
+			copy_to_user ((void *)(rq->ifr_data),
+				&(data_ptr->serial_data),
+				sizeof (data_ptr->serial_data));
+		} else {
+			printk (KERN_ERR "%s: cannot get SDSL info on IDSL card\n", xpds_devs[card_num].name);
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_SET_SDSL_INFO) {
+		if (data_ptr->is_sdsl) {
+			printk ("%s: setting SDSL serial info from user process\n", xpds_devs[card_num].name);
+			copy_from_user (&(data_ptr->serial_data),
+				(void *)(rq->ifr_data),
+				sizeof (data_ptr->serial_data));
+			rc = xpds_set_sdsl_info (card_num);
+			if (rc > 0) {
+				printk (KERN_ERR "%s: unable to set SDSL info\n", xpds_devs[card_num].name);
+				rc = -EBUSY;
+			}
+		} else {
+			printk (KERN_ERR "%s: cannot set SDSL info on IDSL card\n", xpds_devs[card_num].name);
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_GET_SDSL_STATE) {
+		if (data_ptr->is_sdsl) {
+			u8	state;
+			rc = xpds_sdsl_get_state (card_num, &state);
+			if (rc != 0) {
+				printk (KERN_ERR "%s: get SDSL state failed, rc = %d\n", xpds_devs[card_num].name, rc);
+				state = -1;
+				rc = -EBUSY;
+			} else {
+				printk (KERN_NOTICE "%s: SDSL state is 0x%02x\n", xpds_devs[card_num].name, state);
+			}
+			copy_to_user (rq->ifr_data, &state, 1);
+		} else {
+			printk (KERN_ERR "%s: cannot set SDSL state on IDSL card\n", xpds_devs[card_num].name);
+			rc = -EINVAL;
+		}
+	} else if (cmd == XPDS_IOCTL_SET_LOOPBACK) {
+		xpds_loopback_parameters_t	loopback_params;
+		copy_from_user (&loopback_params,
+			(void *)(rq->ifr_data),
+			sizeof (loopback_params));
+		if (loopback_params.loopback_type == XPDS_ASIC_LOOPBACK) {
+			if (loopback_params.on) {
+				printk (KERN_NOTICE "%s: setting ASIC loopback\n", xpds_devs[card_num].name);
+				xpds_write_control_register (card_num, XPDS_MCR_CONFIG,
+					XPDS_MCR_CONFIG__MODE_LOOPBACK, MAIN);
+				netif_start_queue(dev);
+				xpds_data[card_num].physical_up = 1;
+			} else {
+				printk (KERN_NOTICE "%s: unsetting ASIC loopback\n", xpds_devs[card_num].name);
+				xpds_write_control_register (card_num, XPDS_MCR_CONFIG,
+					XPDS_MCR_CONFIG__MODE_NORMAL, MAIN);
+				netif_stop_queue(dev);
+				xpds_if_down(dev);
+				xpds_data[card_num].physical_up = 0;
+			}
+		} else if (loopback_params.loopback_type == XPDS_SDSL_LOOPBACK) {
+			if (data_ptr->is_sdsl) {
+				printk (KERN_NOTICE "%s: setting SDSL external loopback\n", xpds_devs[card_num].name);
+				rc = xpds_sdsl_loopback (card_num);
+				if (rc != 0) {
+					printk (KERN_ERR "%s: setting SDSL external loopback failed, rc = %d\n", xpds_devs[card_num].name, rc);
+					rc = -EBUSY;
+				}
+			} else {
+				printk (KERN_ERR "%s: cannot set SDSL external loopback on IDSL card\n", xpds_devs[card_num].name);
+				rc = -EINVAL;
+			}
+		} else {
+			printk (KERN_ERR "%s: invalid loopback type %d\n",
+				xpds_devs[card_num].name, loopback_params.loopback_type);
+		}
+	} else if (cmd == XPDS_IOCTL_SET_BRIDGED_ETHERNET) {
+		/*
+		 * 1 for bridged ethernet mode, 0 otherwise.
+		 */
+		printk ("%s: bridged ethernet set to %d (%s)\n", xpds_devs[card_num].name,
+			val, val ? "on" : "off");
+		xpds_data[card_num].bridged_ethernet = val;
+	} else if (cmd == XPDS_IOCTL_SET_DLCI_CR) {
+		/*
+		 * Should be 2, except for buggy Ascends which need 0.
+		 */
+		printk ("%s: DLCI CR set to %d\n", xpds_devs[card_num].name, val);
+		xpds_data[card_num].dlci_cr = val;
+	} else if (cmd == XPDS_IOCTL_SET_DLCI_LMI) {
+		/*
+		 * 0 LMI off
+		 * 1 LMI in LT mode (responds to LMI messages)
+		 * 2 LMI in NT mode (generates LMI messages)
+		 * 3 LMI in NT bidirectional mode (generates and
+		 *     responds to LMI messages)
+		 */
+		printk ("%s: DLCI LMI set to %d\n", xpds_devs[card_num].name, val);
+		xpds_data[card_num].dlci_lmi = val;
+	} else if (cmd == XPDS_IOCTL_SET_DLCI) {
+		printk ("%s: DLCI set to %d\n", xpds_devs[card_num].name, val);
+		xpds_data[card_num].dlci = val;
+		xpds_init_frad_data (&(xpds_data[card_num].frad_data), val);
+	} else {
+		printk (KERN_ERR "%s: received cmd = %d, data = %d\n",
+			xpds_devs[card_num].name, cmd, val);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int
+xpds_init_frad_data (struct frad_local *fp, short dlci)
+{
+	memset (&(fp->dlci), 0, sizeof (fp->dlci));
+	fp->dlci[0] = dlci;
+
+	return 0;
+}
+
+static int
+xpds_init (struct net_device *dev)
+{
+	xpds_data_t	*data_ptr;
+	int		card_num;
+
+	card_num = dev - xpds_devs;
+
+	dprintk (KERN_DEBUG "xpds_init (%p (%d))\n", dev, card_num);
+
+	netif_stop_queue(dev);
+	xpds_if_down(dev);
+
+	ether_setup (dev);
+	dev->open = xpds_open;
+	dev->stop = xpds_stop;
+	dev->hard_start_xmit = /* xpds_tx */ xpds_dlci_transmit;
+	dev->get_stats = xpds_get_stats;
+	dev->set_config = xpds_set_config;
+	/* dev->set_mac_address = xpds_set_mac_addr; */
+	/* dev->rebuild_header = xpds_rebuild_header; */
+	dev->do_ioctl = xpds_ioctl;
+	/* dev->change_mtu = xpds_change_mtu; */
+	dev->priv = &(xpds_data[card_num]);
+#if DEBUG
+	if (xpds_debug_level) dev->flags |= IFF_DEBUG;
+#endif
+	dev->flags &= ~ (IFF_BROADCAST | IFF_MULTICAST);
+
+	data_ptr = dev->priv;
+	memset (&(data_ptr->stats), 0, sizeof (data_ptr->stats));
+
+	/* memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); */
+	memcpy(dev->dev_addr,
+		xpds_data[dev - xpds_devs].serial_data.mac_address,
+		sizeof(dev->dev_addr));
+	/* dev->hard_header_len = sizeof (struct frhdr); */
+
+	/*
+	 * Make sure that the payload cannot be equal to or
+	 * greater than the maximum packet length minus headers.
+	 * For some reason, it must be 2 less, not 1 less???
+	 * If dev->mtu from ether_setup() is already smaller,
+	 * leave it alone.
+	 */
+	if (dev->mtu > xpds_max_packet_length - sizeof (struct frhdr) - 2) {
+		dev->mtu             = xpds_max_packet_length - sizeof (struct frhdr) - 2;
+		dprintk (KERN_DEBUG "dev->mtu set to %d\n", dev->mtu);
+	}
+
+	xpds_init_frad_data (&(xpds_data[card_num].frad_data),
+		xpds_default_dlci);
+
+	/*
+	 * Should be set by ioctls...
+	 */
+	xpds_data[card_num].bridged_ethernet = xpds_default_bridged;
+	xpds_data[card_num].dlci = xpds_default_dlci;
+	/* dlci_cr needs to be 0 for buggy Ascends */
+	xpds_data[card_num].dlci_cr = xpds_default_dlci_cr;
+	if (xpds_default_dlci_lmi == XPDS_DLCI_LMI_LT_OR_NT) {
+		if (xpds_data[card_num].is_lt) {
+			xpds_data[card_num].dlci_lmi = XPDS_DLCI_LMI_LT;
+		} else {
+			xpds_data[card_num].dlci_lmi = XPDS_DLCI_LMI_NT;
+		}
+	} else {
+		xpds_data[card_num].dlci_lmi = xpds_default_dlci_lmi;
+	}
+
+	dprintk (KERN_INFO "%s: bridged ethernet mode is %d (%s)\n",
+		xpds_devs[card_num].name, xpds_data[card_num].bridged_ethernet,
+		xpds_data[card_num].bridged_ethernet ? "on" : "off");
+	dprintk (KERN_INFO "%s: DLCI = %d\n",
+		xpds_devs[card_num].name, xpds_data[card_num].dlci);
+	dprintk (KERN_INFO "%s: DLCI CR = %d\n",
+		xpds_devs[card_num].name, xpds_data[card_num].dlci_cr);
+	dprintk (KERN_INFO "%s: DLCI LMI = %d\n",
+		xpds_devs[card_num].name, xpds_data[card_num].dlci_lmi);
+
+	dprintk (KERN_DEBUG "xpds_init done\n");
+
+	return 0;
+}
+
+#define	SEPROM_SIZE		17
+
+#define SEPROM_WRITE_OPCODE	0x5
+#define SEPROM_READ_OPCODE	0x6
+#define SEPROM_ERASE_OPCODE	0x7
+
+#define SEPROM_READ_OPERAND_LEN	9
+#define SEPROM_READ_DATA_LEN	17
+
+/*
+ * Read the given address out of the serial EPROM from an IDSL card.
+ */
+static u16
+read_seprom_data (int card_num, int address)
+{
+	int	i;
+	u16	value = 0;
+	int	opcode;
+
+	if (address < 0 || address >= SEPROM_SIZE) {
+		printk (KERN_ERR "%s: read_seprom_data (%d (invalid))\n",
+			xpds_devs[card_num].name, address);
+		return 0;
+	}
+
+	opcode = (SEPROM_READ_OPCODE << 6) | address;
+
+	/*
+	 * Clear the GPIO.
+	 */
+	xpds_write_control_register (card_num,
+		XPDS_MCR_GPIO_CLR, 0xff, MAIN );
+
+	/*
+	 * Enable the SK, DI, and CS bits in the GPIO,
+	 * and set the CS.
+	 */
+	xpds_write_control_register (card_num,
+		XPDS_MCR_GPIO_SET,
+		XPDS_MCR_GPIO__OE_SEPROM_SK |
+			XPDS_MCR_GPIO__OE_SEPROM_DI |
+			XPDS_MCR_GPIO__GP_SEPROM_CS |
+			XPDS_MCR_GPIO__OE_SEPROM_CS,
+		MAIN );
+	/*
+	 * Give the address to the serial SEPROM.
+	 */
+	for (i = 0; i < SEPROM_READ_OPERAND_LEN; i ++) {
+		int	bit;
+
+		bit = opcode >> ((SEPROM_READ_OPERAND_LEN - 1) - i);
+		bit &= 0x1;
+		bit = bit ? XPDS_MCR_GPIO__GP_SEPROM_DI : 0;
+		/* 0 -> SK, DI */
+		xpds_write_control_register (card_num,
+			XPDS_MCR_GPIO_CLR,
+			XPDS_MCR_GPIO__GP_SEPROM_SK |
+				XPDS_MCR_GPIO__GP_SEPROM_DI,
+			MAIN);
+		udelay (3);
+		/* bit -> DI */
+		xpds_write_control_register (card_num,
+			XPDS_MCR_GPIO_SET, bit, MAIN);
+		/* setup time */
+		udelay (3);
+		/* 1 -> SK */
+		xpds_write_control_register (card_num,
+			XPDS_MCR_GPIO_SET, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN);
+		/* hold time */
+		udelay (3);
+	}
+	/*
+	 * Get the data from the serial SEPROM.
+	 */
+	for (i = 0; i < SEPROM_READ_DATA_LEN; i ++) {
+		u32	bit;
+
+		/* 0 -> SK */
+		xpds_write_control_register (card_num,
+			XPDS_MCR_GPIO_CLR, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN);
+		/* setup time */
+		udelay (3);
+		/* 1 -> SK */
+		xpds_write_control_register (card_num,
+			XPDS_MCR_GPIO_SET, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN);
+		/* hold time */
+		udelay (3);
+		
+		/* read bit */
+		xpds_read_control_register (card_num, XPDS_MCR_GPIO_SET,
+			&bit, MAIN);
+		bit &= XPDS_MCR_GPIO__GP_SEPROM_DO;
+		bit = bit ? 1 : 0;
+		value <<= 1;
+		value |= bit;
+	}
+	value >>= 1;
+
+	/*
+	dprintk (KERN_DEBUG "%s: read_seprom (%d) -> %x\n",
+		xpds_devs[card_num].name, address, value);
+	*/
+
+	return value;
+}
+
+#define SEPROM_REVISION		0
+#define SEPROM_HARDWARE_VERSION	1
+#define SEPROM_SOFTWARE_VERSION	2
+#define SEPROM_FIRMWARE_VERSION	2
+#define SEPROM_MFG_DATE_HI	4
+#define SEPROM_MFG_DATE_LO	5
+#define SEPROM_MAC_ADDR_0_1	6
+#define SEPROM_MAC_ADDR_2_3	7
+#define SEPROM_MAC_ADDR_4_5	8
+#define SEPROM_SERIAL_0		9
+#define SEPROM_SERIAL_1		10
+#define SEPROM_SERIAL_2		11
+#define SEPROM_SERIAL_3		12
+#define SEPROM_SERIAL_4		13
+#define SEPROM_SERIAL_5		14
+#define SEPROM_SERIAL_6		15
+#define SEPROM_SERIAL_7		16
+
+/*
+ * Determine if the card is an IDSL or SDSL card.  Only the IDSL
+ * card has the serial EPROM, so garbage indicates an SDSL card.
+ * Note:  an FPGA card will have is_sdsl set to 1; read_sdsl_info()
+ * will set is_sdsl back to 0 if it appears to be an IDSL FPGA card.
+ * If IDSL, determine if it has the last byte bug.
+ */
+static void
+read_seprom_info (int card_num)
+{
+	u16	value1, value2, value3, value4;
+	xpds_serial_data_t	*sdata;
+
+	if (xpds_data[card_num].is_fpga) {
+		/*
+		 * FPGA card may be SDSL, so set is_sdsl so that
+		 * read_sdsl_info() is called.  read_sdsl_info()
+		 * will unset if unable to get SDSL information.
+		 */
+		xpds_data[card_num].is_sdsl = 1;
+		dprintk (KERN_INFO "No serial EPROM for FPGA device.\n");
+		return;
+	}
+
+	sdata = &(xpds_data[card_num].serial_data);
+
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_0);
+	value2 = read_seprom_data (card_num, SEPROM_SERIAL_1);
+	value3 = read_seprom_data (card_num, SEPROM_SERIAL_2);
+	value4 = read_seprom_data (card_num, SEPROM_SERIAL_3);
+	if (value1 != 0x5854 /* 'XT' */ || value2 != 0x414e /* 'AN' */ ||
+		value3 != 0x3230 /* '20' */ || (value4 >> 8) != '0') {
+		dprintk (KERN_DEBUG "Serial EPROM serial number does not begin with XTAN200; not an IDSL card.\n");    
+		xpds_data[card_num].is_sdsl = 1;
+		return; 
+	}
+
+	value1 = read_seprom_data (card_num, SEPROM_REVISION);
+	dprintk (KERN_DEBUG "Serial EPROM revision %d\n", value1);
+	sdata->seprom_revision = value1;
+
+	value1 = read_seprom_data (card_num, SEPROM_HARDWARE_VERSION);
+	dprintk (KERN_DEBUG "Hardware version %d.%d\n",
+		value1 >> 8, value1 & 0xff);
+	sdata->hardware_version[0] = value1 >> 8;
+	sdata->hardware_version[1] = value1 & 0xff;
+
+	value1 = read_seprom_data (card_num, SEPROM_SOFTWARE_VERSION);
+	dprintk (KERN_DEBUG "Software version %d.%d\n",
+		value1 >> 8, value1 & 0xff);
+	sdata->software_version[0] = value1 >> 8;
+	sdata->software_version[1] = value1 & 0xff;
+
+	value1 = read_seprom_data (card_num, SEPROM_FIRMWARE_VERSION);
+	dprintk (KERN_DEBUG "Firmware version %d.%d\n",
+		value1 >> 8, value1 & 0xff);
+	sdata->firmware_version[0] = value1 >> 8;
+	sdata->firmware_version[1] = value1 & 0xff;
+
+	value1 = read_seprom_data (card_num, SEPROM_MFG_DATE_HI);
+	value2 = read_seprom_data (card_num, SEPROM_MFG_DATE_LO);
+	dprintk (KERN_DEBUG "Manufacturing date %d.%d.%d\n",
+		value1, value2 >> 8, value2 & 0xff);
+	sdata->mfg_date[0] = value1 >> 8;
+	sdata->mfg_date[1] = value1 & 0xff;
+	sdata->mfg_date[2] = value2 >> 8;
+	sdata->mfg_date[3] = value2 & 0xff;
+
+	value1 = read_seprom_data (card_num, SEPROM_MAC_ADDR_0_1);
+	value2 = read_seprom_data (card_num, SEPROM_MAC_ADDR_2_3);
+	value3 = read_seprom_data (card_num, SEPROM_MAC_ADDR_4_5);
+	dprintk (KERN_DEBUG "MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
+		value1 >> 8, value1 & 0xff, value2 >> 8, value2 & 0xff,
+		value3 >> 8, value3 & 0xff);
+	sdata->mac_address[0] = value1 >> 8;
+	sdata->mac_address[1] = value1 & 0xff;
+	sdata->mac_address[2] = value2 >> 8;
+	sdata->mac_address[3] = value2 & 0xff;
+	sdata->mac_address[4] = value3 >> 8;
+	sdata->mac_address[5] = value3 & 0xff;
+
+	dprintk (KERN_DEBUG "Serial number ");
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_0);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[0] = value1 >> 8;
+	sdata->serial_number[1] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_1);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[2] = value1 >> 8;
+	sdata->serial_number[3] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_2);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[4] = value1 >> 8;
+	sdata->serial_number[5] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_3);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[6] = value1 >> 8;
+	sdata->serial_number[7] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_4);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[8] = value1 >> 8;
+	sdata->serial_number[9] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_5);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[10] = value1 >> 8;
+	sdata->serial_number[11] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_6);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[12] = value1 >> 8;
+	sdata->serial_number[13] = value1 & 0xff;
+	value1 = read_seprom_data (card_num, SEPROM_SERIAL_7);
+	dprintk ("%c%c", value1 >> 8, value1 & 0xff);
+	sdata->serial_number[14] = value1 >> 8;
+	sdata->serial_number[15] = value1 & 0xff;
+	dprintk ("\n");
+}
+
+/*
+ * Read information from the SDSL card (previously identified
+ * because the serial EPROM did not exist).
+ * Note that if the mailbox reads fail, it is probably an IDSL
+ * FPGA card.
+ */
+static int
+read_sdsl_info (int card_num)
+{
+	u8	byte, byte1, byte2;
+	int	i, rc, rval = 0;
+	xpds_serial_data_t	*sdata;
+
+	if (! xpds_data[card_num].is_sdsl) return 1;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_HWVER, &byte1);
+	if (rc) rval = 1;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_HWVER, &byte2);
+	if (rc) rval = 1;
+
+	if (rval) {
+		/* IDSL FPGA card */
+		xpds_data[card_num].is_sdsl = 0;
+		return rval;
+	}
+
+	sdata = &(xpds_data[card_num].serial_data);
+
+	dprintk (KERN_INFO "Hardware version ");
+	dprintk ("%d.", byte1);
+	dprintk ("%d\n", byte2);
+
+	sdata->hardware_version[0] = byte1;
+	sdata->hardware_version[1] = byte2;
+
+	dprintk (KERN_INFO "Firmware version ");
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, &byte);
+	dprintk ("%d.", byte);
+	if (rc) rval = 1;
+	sdata->firmware_version[0] = byte;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, &byte);
+	dprintk ("%d\n", byte);
+	if (rc) rval = 1;
+	sdata->firmware_version[1] = byte;
+
+	if (rval) return rval;
+
+	dprintk (KERN_INFO "Manufacturing date ");
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte1);
+	if (rc) rval = 1;
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte2);
+	if (rc) rval = 1;
+	dprintk ("%d.", (byte1 << 8) + byte2);
+	sdata->mfg_date[0] = byte1;
+	sdata->mfg_date[1] = byte2;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte);
+	if (rc) rval = 1;
+	dprintk ("%d.", byte);
+	sdata->mfg_date[2] = byte;
+
+	rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte);
+	if (rc) rval = 1;
+	dprintk ("%d\n", byte);
+	sdata->mfg_date[3] = byte;
+
+	if (rval) return rval;
+
+	dprintk (KERN_INFO "MAC address ");
+	for (i = 0; i < 6; i ++) {
+		if (i != 0) dprintk (":");
+		rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MACADDR, &byte);
+		dprintk ("%02x", byte);
+		if (rc) rval = 1;
+		sdata->mac_address[i] = byte;
+	}
+	dprintk ("\n");
+
+	if (rval) return rval;
+
+	dprintk (KERN_INFO "Serial number ");
+	for (i = 0; i < 16; i ++) {
+		rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_SERIALNUMBER, &byte);
+		dprintk ("%c", byte);
+		if (rc) rval = 1;
+		sdata->serial_number[i] = byte;
+	}
+	dprintk ("\n");
+
+	return rval;
+}
+
+static int
+get_pci_config (struct pci_dev *dev, volatile void **config_mem)
+{
+	u32	value;
+	int	rc;
+
+	rc = pci_read_config_dword (dev, PCI_BASE_ADDRESS_0, &value);
+
+	if (rc != PCIBIOS_SUCCESSFUL) {
+		printk (KERN_ERR "unable to read PCI configuration register 4, error = %d\n", rc);
+		return -EIO;
+	}
+
+	dprintk (KERN_DEBUG "PCI config register 4 value=%08x\n", value);
+
+	value &= PCI_BASE_ADDRESS_IO_MASK;
+ 
+        *config_mem = ioremap (value, 32768);
+ 
+        dprintk (KERN_DEBUG "remapped hw_config_mem=%08x\n", (u32) *config_mem);
+#if (LINUX_VERSION_CODE < 0x02030d)
+        dprintk (KERN_DEBUG "dev->base_address[0]=%08x\n", (u32) dev->base_address[0]);
+        dprintk (KERN_DEBUG "dev->base_address[1]=%08x\n", (u32) dev->base_address[1]);
+        dprintk (KERN_DEBUG "dev->base_address[2]=%08x\n", (u32) dev->base_address[2]);
+        dprintk (KERN_DEBUG "dev->base_address[3]=%08x\n", (u32) dev->base_address[3]);
+        dprintk (KERN_DEBUG "dev->base_address[4]=%08x\n", (u32) dev->base_address[4]);
+        dprintk (KERN_DEBUG "dev->base_address[5]=%08x\n", (u32) dev->base_address[5]);
+#else
+        dprintk (KERN_DEBUG "dev->base_address[0]=%08x\n", (u32) dev->resource[0].start);
+        dprintk (KERN_DEBUG "dev->base_address[1]=%08x\n", (u32) dev->resource[1].start);
+        dprintk (KERN_DEBUG "dev->base_address[2]=%08x\n", (u32) dev->resource[2].start);
+        dprintk (KERN_DEBUG "dev->base_address[3]=%08x\n", (u32) dev->resource[3].start);
+        dprintk (KERN_DEBUG "dev->base_address[4]=%08x\n", (u32) dev->resource[4].start);
+        dprintk (KERN_DEBUG "dev->base_address[5]=%08x\n", (u32) dev->resource[5].start);
+#endif
+
+        return config_mem == NULL ? -ENOMEM : 0;
+}
+
+#define TRIES	8
+
+static int
+allocate_rxtx_buffers (int xpds_num, int num_rxtx, volatile void **rxtx_mem,
+	volatile xpds_rxtx_list_t **rx_list,
+	volatile xpds_rxtx_list_t **tx_list)
+{
+	int	size, i, j;
+	volatile u8	*rx_buffer, *tx_buffer;
+	volatile xpds_rxtx_list_t	*rx_ptr, *tx_ptr;
+	volatile void	*old_mem[TRIES];
+
+	size = (num_rxtx + num_rxtx) * sizeof (xpds_rxtx_list_t) +
+		(num_rxtx + num_rxtx) * RXTX_BUFFER_SIZE;
+
+	/*
+	 * Prevent RX/TX memory from crossing a 16 bit page (64k
+	 * byte) boundary.  This is due to a hardware limitation.
+	 */
+	memset (old_mem, 0, sizeof (old_mem));
+	for (i = 0; i < TRIES; i ++) {
+		u32	start, end;
+
+		*rxtx_mem = kmalloc (size + 16, GFP_KERNEL /* | GFP_DMA */);
+		if (*rxtx_mem == NULL) break;
+		
+		start = virt_to_bus (*rxtx_mem);
+		end = start + size + 16;
+
+		start &= 0xffff;
+		end &= 0xffff;
+
+		if (start < end) break;
+
+		printk (KERN_DEBUG "%s: RX/TX buffer allocation crossed 16 bit boundary, reallocating.\n", xpds_devs[xpds_num].name);
+		old_mem[i] = *rxtx_mem;
+	}
+	for (j = 0; j < i; j ++) {
+		if (old_mem[j] != NULL) kfree ((void *)(old_mem[j]));
+	}
+	if (i == TRIES || *rxtx_mem == NULL) return -ENOMEM;
+
+	xpds_data[xpds_num].rxtx_mem_allocated = *rxtx_mem;
+
+	if ((u32)*rxtx_mem & 0xf) {
+		*rxtx_mem = (volatile void *) (((u32) *rxtx_mem + 16) & 0xfffffff0);
+	}
+
+	*rx_list = (volatile xpds_rxtx_list_t *) *rxtx_mem;
+	*tx_list = (volatile xpds_rxtx_list_t *) ((u32) *rxtx_mem +
+		sizeof (xpds_rxtx_list_t) * num_rxtx);
+
+	rx_buffer = (volatile u8 *) ((u32) *tx_list +
+		sizeof (xpds_rxtx_list_t) * num_rxtx);
+	tx_buffer = (volatile u8 *) ((u32) rx_buffer +
+		RXTX_BUFFER_SIZE * num_rxtx);
+
+	rx_ptr = *rx_list;
+	tx_ptr = *tx_list;
+
+	for (i = 0; i < num_rxtx; i ++) {
+		rx_ptr->control = RXTX_CONTROL__NEXT_VALID | RXTX_CONTROL__HWGO;
+		rx_ptr->buffer = rx_buffer + i * RXTX_BUFFER_SIZE;
+		rx_ptr->buffer_bus_addr = virt_to_bus (rx_ptr->buffer);
+		rx_ptr->next = (i == num_rxtx - 1) ?
+			*rx_list : 
+			(volatile xpds_rxtx_list_t *) ((u32) rx_ptr +
+				sizeof (xpds_rxtx_list_t));
+		rx_ptr->next_bus_addr = virt_to_bus (rx_ptr->next);
+		rx_ptr->unused1 = 0;
+		rx_ptr->prev = (i == 0) ?
+			(volatile xpds_rxtx_list_t *) ((u32) *rx_list +
+				sizeof (xpds_rxtx_list_t) * (num_rxtx - 1)) :
+			(volatile xpds_rxtx_list_t *) ((u32) rx_ptr -
+				sizeof (xpds_rxtx_list_t));
+		rx_ptr->offset = 0;
+		rx_ptr = rx_ptr->next;
+
+		tx_ptr->control = RXTX_CONTROL__NEXT_VALID;
+		tx_ptr->buffer = tx_buffer + i * RXTX_BUFFER_SIZE;
+		tx_ptr->buffer_bus_addr = virt_to_bus (tx_ptr->buffer);
+		tx_ptr->next = (i == num_rxtx - 1) ?
+			*tx_list : 
+			(volatile xpds_rxtx_list_t *) ((u32) tx_ptr +
+				sizeof (xpds_rxtx_list_t));
+		tx_ptr->next_bus_addr = virt_to_bus (tx_ptr->next);
+		tx_ptr->unused1 = 0;
+		tx_ptr->prev = (i == 0) ?
+			(volatile xpds_rxtx_list_t *) ((u32) *tx_list +
+				sizeof (xpds_rxtx_list_t) * (num_rxtx - 1)) :
+			(volatile xpds_rxtx_list_t *) ((u32) tx_ptr -
+				sizeof (xpds_rxtx_list_t));
+		tx_ptr->offset = 0;
+		tx_ptr = tx_ptr->next;
+	}
+
+	dprintk (KERN_DEBUG "rxtx_mem = %08x, rx_list = %08x, tx_list = %08x,\n",
+		(u32) *rxtx_mem, (u32) *rx_list, (u32) *tx_list);
+	dprintk (KERN_DEBUG "rx_buffer = %08x, tx_buffer = %08x\n",
+		(u32) rx_buffer, (u32) tx_buffer);
+	return 0;
+}
+
+/*
+ * Given a pointer to memory, set the control register pointers.
+ */
+static int
+set_control_register_pointers (int xpds_num, volatile void *mem,
+	volatile xpds_rxtx_list_t *rx_list, volatile xpds_rxtx_list_t *tx_list)
+{
+	int	rc;
+
+	dprintk (KERN_DEBUG "setting %s: control register pointers\n", xpds_devs[xpds_num].name);
+	xpds_data[xpds_num].main_control_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0000 );
+	xpds_data[xpds_num].rx_fifo_control_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0050 );
+	xpds_data[xpds_num].tx_fifo_control_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0040 );
+	xpds_data[xpds_num].rx_dma_control_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x00c0 );
+	xpds_data[xpds_num].tx_dma_control_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0080 );
+	xpds_data[xpds_num].rx_fifo_data_registers =
+		(volatile u32 *) ( (volatile u8 *) mem +
+		(xpds_data[xpds_num].is_fpga ? 0x0600 : 0x0500) );
+	xpds_data[xpds_num].tx_fifo_data_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0400 );
+	xpds_data[xpds_num].aux_registers =
+		(volatile u32 *) ( (volatile u8 *) mem + 0x0060 );
+	dprintk (KERN_DEBUG "memory=%08x\n", (u32)mem);
+	dprintk (KERN_DEBUG "main_control_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].main_control_registers));
+	dprintk (KERN_DEBUG "rx_fifo_control_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].rx_fifo_control_registers));
+	dprintk (KERN_DEBUG "tx_fifo_control_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].tx_fifo_control_registers));
+	dprintk (KERN_DEBUG "rx_dma_control_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].rx_dma_control_registers));
+	dprintk (KERN_DEBUG "tx_dma_control_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].tx_dma_control_registers));
+	dprintk (KERN_DEBUG "rx_fifo_data_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].rx_fifo_data_registers));
+	dprintk (KERN_DEBUG "tx_fifo_data_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].tx_fifo_data_registers));
+	dprintk (KERN_DEBUG "aux_registers=%08x\n",
+		(u32)(xpds_data[xpds_num].aux_registers));
+
+	/*
+	 * Install the buffer lists for RX and TX DMA.
+	 */
+	xpds_data[xpds_num].rx_dma_list = rx_list;
+	dprintk (KERN_DEBUG "initializing %s: RX DMA Status Address:\n", xpds_devs[xpds_num].name);
+	rc = xpds_write_control_register (xpds_num, XPDS_DMA_STAT_ADDR,
+		(u32) rx_list, RX_DMA);
+	if (rc != 0) return rc;
+	dprintk ("\n");
+	xpds_data[xpds_num].tx_dma_list = tx_list;
+	dprintk (KERN_DEBUG "initializing %s: TX DMA Status Address:\n", xpds_devs[xpds_num].name);
+	rc = xpds_write_control_register (xpds_num, XPDS_DMA_STAT_ADDR,
+		(u32) tx_list, TX_DMA);
+	dprintk ("\n");
+	if (rc != 0) return rc;
+
+	return 0;
+}
+
+static int
+xpds_alloc_data (void)
+{
+	int	rc;
+
+	if (xpds_data == NULL) {
+		xpds_data = kmalloc (sizeof (*xpds_data) * xpds_max_cards, GFP_KERNEL);
+		if (xpds_data == NULL) return -ENOMEM;
+		memset (xpds_data, 0, sizeof (*xpds_data) * xpds_max_cards);
+	}
+	if (xpds_names == NULL) {
+		xpds_names = kmalloc (NAME_SIZE * xpds_max_cards, GFP_KERNEL);
+		if (xpds_names == NULL) return -ENOMEM;
+		memset (xpds_names, 0, NAME_SIZE * xpds_max_cards);
+	}
+	if (xpds_reset_bh_tasks == NULL) {
+		xpds_reset_bh_tasks = kmalloc (sizeof (*xpds_reset_bh_tasks) * xpds_max_cards, GFP_KERNEL);
+		if (xpds_reset_bh_tasks == NULL) return -ENOMEM;
+		memset (xpds_reset_bh_tasks, 0, sizeof (*xpds_reset_bh_tasks) * xpds_max_cards);
+	}
+	if (xpds_interrupt_bh_tasks == NULL) {
+		int	i;
+		xpds_interrupt_bh_tasks = kmalloc (sizeof (*xpds_interrupt_bh_tasks) * xpds_max_cards, GFP_KERNEL);
+		if (xpds_interrupt_bh_tasks == NULL) return -ENOMEM;
+		memset (xpds_interrupt_bh_tasks, 0, sizeof (*xpds_interrupt_bh_tasks) * xpds_max_cards);
+		for (i = 0; i < xpds_max_cards; i ++) {
+			xpds_interrupt_bh_tasks[i].data = kmalloc (sizeof (interrupt_bh_data_t), GFP_KERNEL);
+			xpds_interrupt_bh_tasks[i].next = NULL;
+			xpds_interrupt_bh_tasks[i].sync = 0;
+			xpds_interrupt_bh_tasks[i].routine = xpds_interrupt_bh;
+		}
+	}
+	if (xpds_devs == NULL) {
+		int	i;
+		xpds_devs = kmalloc (sizeof (*xpds_devs) * xpds_max_cards, GFP_KERNEL);
+		if (xpds_devs == NULL) return -ENOMEM;
+		memset (xpds_devs, 0, sizeof (*xpds_devs) * xpds_max_cards);
+
+		for (i = 0; i < xpds_max_cards; i ++) {
+#if (LINUX_VERSION_CODE < HAS_SOFT_NET)
+                        xpds_devs[i].name = xpds_names + NAME_SIZE * i;
+#endif
+			xpds_devs[i].init = xpds_init;
+		}
+	}
+	rc = xpds_sdsl_allocate ();
+	if (rc) return rc;
+	return 0;
+}
+
+static void
+xpds_free_data (void)
+{
+	if (xpds_data != NULL) kfree (xpds_data);
+	if (xpds_names != NULL) kfree (xpds_names);
+	if (xpds_reset_bh_tasks != NULL) kfree (xpds_reset_bh_tasks);
+	if (xpds_devs != NULL) kfree (xpds_devs);
+	xpds_sdsl_cleanup ();
+}
+
+#ifdef MODULE
+int init_module (void)
+#else
+int xpds_init(void)
+#endif
+{
+	int		i;
+	int		rc;
+	volatile xpds_rxtx_list_t	*rx_list, *tx_list;
+	volatile void	*rxtx_mem;
+	int		found_sdsl = 0;
+	struct pci_dev	*dev = NULL;
+#if ALLOW_OLD_PCI_VENDOR_ID
+	int		old_offset = -1;
+#endif
+
+	printk (KERN_NOTICE "xpds.c: Xpeed XPDS frame relay driver %s, linux@xpeed.com, Copyright 1999, 2000 Xpeed, Inc.\n", VERSION_STRING);
+	rc = xpds_alloc_data ();
+	if (rc != 0) {
+		printk (KERN_ERR "Unable to allocate memory for XPDS data.\n");
+		xpds_free_data ();
+		return rc;
+	}
+
+	dprintk (KERN_DEBUG "xpds_data = %p, &(xpds_data[0].rxci_interrupt_received) = %p\n", xpds_data, &(xpds_data[0].rxci_interrupt_received));
+
+	/*
+	 * Make sure that there is a PCI present, and the
+	 * desired device is there.
+	 */
+	if ( ! pci_present ()) {
+		printk (KERN_ERR "No PCI present.\n");
+		xpds_free_data ();
+		return -ENODEV;
+	}
+
+	for (i = 0; i < xpds_max_cards; i ++) {
+		char	devname[NAME_SIZE], name[NAME_SIZE];
+		int	n;
+
+		sprintf (devname, "%.*s%%d", NAME_SIZE - 4, xpds_dev_name);
+		n = dev_alloc_name (&(xpds_devs[i]), devname);
+		if (n < 0) {
+			printk(KERN_ERR "%s: dev_alloc_name failed (%d)\n", xpds_dev_name, n);
+			xpds_free_data ();
+			return -ENODEV;
+		}
+		sprintf (name, "%.*s%d", NAME_SIZE - 4, xpds_dev_name, n);
+		strcpy (xpds_devs[i].name, name);
+	}
+
+	for (i = 0; i < xpds_max_cards; i ++) {
+		memset (&(xpds_data[i]), 0, sizeof (xpds_data[i]));
+		xpds_data[i].speed_mode = xpds_mode;
+
+#if ALLOW_OLD_PCI_VENDOR_ID
+		if (old_offset < 0) {
+			dev = pci_find_device (PCI_VENDOR_ID_XPDS,
+				PCI_DEVICE_ID_XPDS_1, dev);
+			if (dev == NULL) {
+				old_offset = i;
+				dev = pci_find_device (PCI_VENDOR_ID_XPDS_OLD,
+					PCI_DEVICE_ID_XPDS_1, dev);
+			}
+		} else {
+			dev = pci_find_device (PCI_VENDOR_ID_XPDS_OLD,
+				PCI_DEVICE_ID_XPDS_1, dev);
+		}
+#else
+		dev = pci_find_device (PCI_VENDOR_ID_XPDS,
+			PCI_DEVICE_ID_XPDS_1, dev);
+#endif
+		if (dev == NULL) break;
+		dprintk (KERN_DEBUG "dev = %p\n", dev);
+
+		xpds_data[i].pci_dev = dev;
+
+		pci_set_master (dev);
+
+#if ALLOW_OLD_PCI_VENDOR_ID
+		if (old_offset >= 0) {
+			dprintk (KERN_DEBUG "XPDS FPGA device %d at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n",
+				i,
+				xpds_data[i].pci_dev->bus->number,
+				xpds_data[i].pci_dev->devfn,
+				xpds_data[i].pci_dev->irq);
+			xpds_data[i].is_fpga = 1;
+		} else {
+			dprintk (KERN_DEBUG "XPDS device %d found at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n",
+				i,
+				xpds_data[i].pci_dev->bus->number,
+				xpds_data[i].pci_dev->devfn,
+				xpds_data[i].pci_dev->irq);
+			xpds_data[i].is_fpga = 0;
+		}
+#else
+		dprintk (KERN_DEBUG "XPDS device %d found at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n",
+			i,
+			xpds_data[i].pci_dev->bus->number,
+			xpds_data[i].pci_dev->devfn,
+			xpds_data[i].pci_dev->irq);
+		xpds_data[i].is_fpga = 0;
+#endif
+		
+		/*
+		 * Get the 32k of memory from hardware.
+		 */
+		dprintk (KERN_DEBUG "%s: setting up PCI configuration\n", xpds_devs[i].name);
+		rc = get_pci_config (xpds_data[i].pci_dev,
+			&(xpds_data[i].config_mem_remapped));
+		if (rc != 0) {
+			xpds_free_data ();
+			return rc;
+		}
+
+		/*
+		 * Allocate the RX and TX lists and buffers.
+		 */
+		dprintk (KERN_DEBUG "%s: allocating RX/TX buffers\n", xpds_devs[i].name);
+		rc = allocate_rxtx_buffers (i, NUM_DESC, &rxtx_mem,
+			&rx_list, &tx_list);
+		if (rc != 0) {
+			xpds_free_data ();
+			return rc;
+		}
+
+		/*
+		 * Set up the control register pointers to the RX/TX buffers.
+		 */
+		rc = set_control_register_pointers (i,
+			xpds_data[i].config_mem_remapped, rx_list, tx_list);
+		if (rc != 0) {
+			xpds_free_data ();
+			return rc;
+		}
+
+		/*
+		 * Read the serial EPROM for some more information
+		 * about the device.  May set xpds_data[i].is_sdsl or
+		 * xpds_data[i].has_last_byte_bug .
+		 */
+		read_seprom_info (i);
+
+		/*
+		 * If the serial EPROM read failed, then it should be
+		 * an SDSL device.  Read the information from the SDSL
+		 * device.  If an error, then something is wrong...
+		 */
+		if (xpds_data[i].is_sdsl) {
+			xpds_reset_sdsl (i);
+			xpds_start_sdsl (i);
+			DELAY_HZ (3 * HZ / 2, i);
+			rc = read_sdsl_info (i);
+			if (rc != 0) {
+				if (xpds_data[i].is_sdsl) {
+					printk (KERN_ERR "%s: Unable to determine if %s is an IDSL or SDSL card.\n", xpds_devs[i].name, xpds_devs[i].name);
+					printk (KERN_ERR "%s: %s may be defective\n", xpds_devs[i].name, xpds_devs[i].name);
+					if (! xpds_load_for_flash) {
+						xpds_free_data ();
+						return -ENODEV;
+					}
+				} else {
+					/* FPGA IDSL board */
+					printk (KERN_DEBUG "%s: %s has no SDSL infomation.\n", xpds_devs[i].name, xpds_devs[i].name);
+					printk (KERN_DEBUG "%s: is assumed to be an FPGA IDSL card.\n", xpds_devs[i].name);
+				}
+			}
+		}
+
+		/*
+		 * Hardware bugs:
+		 * ASIC < 1.1:  last byte corruption bug
+		 * hardware < 1.1:  RX DMA burst bug
+		 * hardware < 1.2:  TX DMA burst bug
+		 */
+		if (! xpds_data[i].is_fpga && ! xpds_is_hardware_version (i, 1, 1)) {
+			dprintk (KERN_DEBUG "Has last byte bug, using workaround.\n");
+			xpds_data[i].has_last_byte_bug = 1;
+		}
+
+		if (! xpds_is_hardware_version (i, 1, 1)) {
+			dprintk (KERN_DEBUG "Has RX DMA burst bug, not using RX DMA burst mode.\n");
+			xpds_data[i].has_rx_dma_burst_bug = 1;
+		}
+
+		if (! xpds_is_hardware_version (i, 1, 2)) {
+			dprintk (KERN_DEBUG "Has TX DMA burst bug, not using TX DMA burst mode.\n");
+			xpds_data[i].has_tx_dma_burst_bug = 1;
+			dprintk (KERN_DEBUG "Has TX DMA low rate (<%d Kbps) bug.\n", LOW_BIT_RATE);
+			xpds_data[i].has_tx_dma_low_rate_bug = 1;
+		}
+
+		printk (KERN_DEBUG "%s: Xpeed %c00 %cDSL NIC, %02X %02X %02X %02X %02X %02X, IRQ %d.\n",
+			xpds_devs[i].name,
+			xpds_data[i].is_sdsl ? '3' : '2',
+			xpds_data[i].is_sdsl ? 'S' : 'I',
+			xpds_data[i].serial_data.mac_address[0],
+			xpds_data[i].serial_data.mac_address[1],
+			xpds_data[i].serial_data.mac_address[2],
+			xpds_data[i].serial_data.mac_address[3],
+			xpds_data[i].serial_data.mac_address[4],
+			xpds_data[i].serial_data.mac_address[5],
+			xpds_data[i].pci_dev->irq);
+
+		if (xpds_data[i].is_sdsl) {
+			int	rc;
+			u32	mode, speed, swap, invert, nt;
+
+			rc = xpds_get_flash_sdsl_mode (i, &mode);
+
+			if (rc) {
+				printk (KERN_ERR "%s: unable to get speed, swap, and invert\n", xpds_devs[i].name);
+			} else {
+				speed = (mode & XPDS_SDSL_MODE__SPEED_MASK) << 3;
+				nt = (mode & XPDS_SDSL_MODE__NT);
+				swap = (mode & XPDS_SDSL_MODE__SWAP);
+				invert = (mode & XPDS_SDSL_MODE__INVERT);
+				printk (KERN_DEBUG "%s: speed = %d%s, swap %s, invert %s, %s mode\n", xpds_devs[i].name, speed, (speed == 0) ? " (CM auto)" : "", swap ? "on" : "off", invert ? "on" : "off", nt ? "NT" : "LT");
+			}
+		}
+
+		if (xpds_data[i].is_sdsl) found_sdsl = 1;
+
+		/*
+		 * Register network device.
+		 */
+		rc = register_netdev (&(xpds_devs[i]));
+		if (rc != 0) {
+			xpds_free_data ();
+			return rc;
+		}
+	}
+
+	num_xpds_found = i;
+
+	if (num_xpds_found < 1) {
+		printk (KERN_ERR "PCI error:  XPDS device not found.\n");
+		xpds_free_data ();
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+void cleanup_module (void)
+{
+	int	i;
+
+	for (i = 0; i < num_xpds_found; i ++) {
+		if (xpds_data[i].rxtx_mem_allocated != NULL) {
+			kfree ((void *)(xpds_data[i].rxtx_mem_allocated));
+		}
+		if (xpds_data[i].config_mem_remapped != NULL) {
+			iounmap ((void *)(xpds_data[i].config_mem_remapped));
+		}
+		dprintk (KERN_DEBUG "unregistering XPDS network device %d\n", i);
+		unregister_netdev (&(xpds_devs[i]));
+	}
+	xpds_free_data ();
+	dprintk (KERN_DEBUG "removing Xpeed XPDS frame relay driver\n");
+}
diff -urN linux-2.2.18-pre15/drivers/net/xpds/xpds.h linux/drivers/net/xpds/xpds.h
--- linux-2.2.18-pre15/drivers/net/xpds/xpds.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/xpds/xpds.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1998, 1999, 2000 Xpeed, Inc.
+ * xpds-fr.h, $Revision: 1.10 $
+ */
+#ifndef XPDS_H
+#define XPDS_H 1
+
+#include <asm/types.h>
+#include "xpds-encap-fr.h"
+#include <linux/xpds-ioctl.h>
+
+extern int xpds_max_cards;
+
+#define DEBUG_MAIN	1
+#define DEBUG_FSM	2
+#define DEBUG_DETAILED	128
+
+extern int	xpds_debug_level;
+
+typedef struct xpds_rxtx_list_t {
+	volatile u32		control;
+	u32			buffer_bus_addr;
+	u32			next_bus_addr;
+	u32			unused1;
+	volatile u8		*buffer;
+	volatile struct xpds_rxtx_list_t	*next;
+	volatile struct xpds_rxtx_list_t	*prev;
+	u32			offset;
+} xpds_rxtx_list_t;
+
+typedef struct {
+	struct pci_dev		*pci_dev;
+	volatile u32   		*main_control_registers;
+	volatile u32   		*rx_fifo_control_registers;
+	volatile u32   		*tx_fifo_control_registers;
+	volatile u32   		*rx_dma_control_registers;
+	volatile u32   		*tx_dma_control_registers;
+	volatile u32   		*rx_fifo_data_registers;
+	volatile u32   		*tx_fifo_data_registers;
+	volatile u32   		*aux_registers;
+	volatile void		*rxtx_mem_allocated;
+	volatile xpds_rxtx_list_t	*rx_dma_list;
+	volatile xpds_rxtx_list_t	*tx_dma_list;
+	volatile xpds_rxtx_list_t	*current_rx_dma;
+	volatile xpds_rxtx_list_t	*current_tx_dma;
+	struct net_device_stats	stats;
+	volatile int		rxci_interrupt_received;
+	int			physical_up;
+	int			physical_retrying;
+	int			is_fpga;
+	int			has_last_byte_bug;
+	int			is_sdsl;
+	int			is_lt;
+	int			speed_mode;
+	short			dlci;
+	xpds_serial_data_t	serial_data;
+	int			has_rx_dma_burst_bug;
+	int			has_tx_dma_burst_bug;
+	volatile void		*config_mem_remapped;
+	int			has_tx_dma_low_rate_bug;
+	int			sdsl_speed;
+	int			current_tx_fifo;
+	volatile xpds_rxtx_list_t	*current_hw_tx_dma;
+	int			current_rx_fifo;
+	volatile xpds_rxtx_list_t	*current_hw_rx_dma;
+	int			has_rx_dma_low_rate_bug;
+	struct frad_local	frad_data;
+	int			bridged_ethernet;
+	u8			dlci_cr;
+	u8			dlci_lmi;
+	dlci_lmi_timer_data_t	dlci_lmi_timer_data[CONFIG_DLCI_COUNT];
+	struct timer_list	dlci_lmi_timers[CONFIG_DLCI_COUNT];
+} xpds_data_t;
+
+extern struct net_device *xpds_devs;
+extern xpds_data_t *xpds_data;
+
+extern int xpds_read_control_register_quiet (int xpds_num, int register_number,
+	u32 *value, int which);
+extern int xpds_write_control_register_quiet (int xpds_num, int register_number,
+	u32 value, int which);
+extern int xpds_read_control_register (int xpds_num, int register_number,
+	u32 *value, int which);
+extern int xpds_write_control_register (int xpds_num, int register_number,
+	u32 value, int which);
+
+#define XPDS_MAIN    		0
+#define XPDS_RX_FIFO		2
+#define XPDS_TX_FIFO		3
+#define XPDS_RX_DMA		4
+#define XPDS_TX_DMA  		5
+#define XPDS_RX_FIFO_DATA	6
+#define XPDS_TX_FIFO_DATA	7
+#define XPDS_AUX		8
+
+#define MAIN            XPDS_MAIN
+#define RX_FIFO         XPDS_RX_FIFO
+#define TX_FIFO         XPDS_TX_FIFO
+#define RX_DMA          XPDS_RX_DMA
+#define TX_DMA          XPDS_TX_DMA
+#define RX_FIFO_DATA    XPDS_RX_FIFO_DATA
+#define TX_FIFO_DATA    XPDS_TX_FIFO_DATA
+#define AUX             XPDS_AUX
+
+extern int xpds_tx (u8 *buffer, unsigned int len, struct net_device *dev);
+
+#ifdef __SMP__
+#define schedule_if_no_interrupt(card_num) \
+	do { \
+	} while (0)
+#else
+#define schedule_if_no_interrupt(card_num) \
+	do { \
+		if (!in_interrupt()) schedule (); \
+	} while (0)
+#endif
+
+#define DELAY(n,card_num)	schedule_timeout(HZ*(n))
+#define DELAY_HZ(n,card_num)	schedule_timeout(n)
+
+#endif
diff -urN linux-2.2.18-pre15/drivers/nubus/Makefile linux/drivers/nubus/Makefile
--- linux-2.2.18-pre15/drivers/nubus/Makefile	Sat Mar 21 11:06:51 1998
+++ linux/drivers/nubus/Makefile	Mon Dec  3 20:34:11 2001
@@ -9,7 +9,19 @@
 # parent makefile.
 #
 
-L_OBJS   := nubus.o
 L_TARGET := nubus.a
+
+ifeq ($(CONFIG_MODULES),y)
+O_TARGET := nubus_n_syms.o
+OX_OBJS  := nubus_syms.o
+O_OBJS   := nubus.o
+L_OBJS   := nubus_n_syms.o
+else
+L_OBJS   := nubus.o
+endif
+
+ifdef CONFIG_PROC_FS
+L_OBJS   += proc.o
+endif
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.18-pre15/drivers/nubus/nubus.c linux/drivers/nubus/nubus.c
--- linux-2.2.18-pre15/drivers/nubus/nubus.c	Tue Dec 29 11:32:06 1998
+++ linux/drivers/nubus/nubus.c	Mon Dec  3 20:34:11 2001
@@ -1,78 +1,112 @@
 /*
  *	Macintosh Nubus Interface Code
+ *
+ *      Originally by Alan Cox
+ *
+ *      Mostly rewritten by David Huggins-Daines, C. Scott Ananian,
+ *      and others.
  */
 
 #include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/bios32.h>
-#include <linux/pci.h>
 #include <linux/string.h>
 #include <linux/nubus.h>
 #include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
 #include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/page.h>
 #include <asm/hwtest.h>
-/* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */
-#include <asm/macintosh.h>
 #include <linux/proc_fs.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
 
+extern void via_nubus_init(void);
+extern void oss_nubus_init(void);
 
-#undef LCIII_WEIRDNESS
- 
-static struct nubus_slot nubus_slots[16];
- 
-/*
- *	Please skip to the bottom of this file if you ate lunch recently
- *				-- Alan
- */
- 
+/* Constants */
 
-/*
- *	Yes this sucks. The ROM can appear on arbitary bytes of the long
- *	word. We are not amused.
- */
+/* This is, of course, the size in bytelanes, rather than the size in
+   actual bytes */
+#define FORMAT_BLOCK_SIZE 20
+#define ROM_DIR_OFFSET 0x24
+
+#define NUBUS_TEST_PATTERN 0x5A932BC7
+
+/* Globals */
+
+struct nubus_dev*   nubus_devices;
+struct nubus_board* nubus_boards;
+
+extern int console_loglevel;
+
+/* Meaning of "bytelanes":
+
+   The card ROM may appear on any or all bytes of each long word in
+   NuBus memory.  The low 4 bits of the "map" value found in the
+   format block (at the top of the slot address space, as well as at
+   the top of the MacOS ROM) tells us which bytelanes, i.e. which byte
+   offsets within each longword, are valid.  Thus:
+
+   A map of 0x0f, as found in the MacOS ROM, means that all bytelanes
+   are valid.
+
+   A map of 0xf0 means that no bytelanes are valid (We pray that we
+   will never encounter this, but stranger things have happened)
+
+   A map of 0xe1 means that only the MSB of each long word is actually
+   part of the card ROM.  (We hope to never encounter NuBus on a
+   little-endian machine.  Again, stranger things have happened)
+
+   A map of 0x78 means that only the LSB of each long word is valid.
+
+   Etcetera, etcetera.  Hopefully this clears up some confusion over
+   what the following code actually does.  */
  
-extern __inline__ int not_useful(void *p, int map)
+extern inline int not_useful(void *p, int map)
 {
 	unsigned long pv=(unsigned long)p;
-	pv&=3;
-	if(map&(1<<pv))
+	pv &= 3;
+	if(map & (1<<pv))
 		return 0;
 	return 1;
 }
  
 static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
 {
-	unsigned long v=0;
-	unsigned char *p=*ptr;	/* as v|=*((*ptr)++) upset someone */
+	/* This will hold the result */
+	unsigned long v = 0;
+	unsigned char *p = *ptr;
+
 	while(len)
 	{
-		v<<=8;
+		v <<= 8;
 		while(not_useful(p,map))
 			p++;
-		v|=*p++;
+		v |= *p++;
 		len--;
 	}
-	*ptr=p;
+	*ptr = p;
 	return v;
 }
 
 static void nubus_rewind(unsigned char **ptr, int len, int map)
 {
 	unsigned char *p=*ptr;
-	
-	if(len>65536)
-		printk("rewind of %d!\n", len);
+
+	/* Sanity check */
+	if(len > 65536)
+		printk(KERN_ERR "rewind of 0x%08x!\n", len);
 	while(len)
 	{
 		do
 		{
 			p--;
 		}
-		while(not_useful(p,map));
+		while(not_useful(p, map));
 		len--;
 	}
 	*ptr=p;
@@ -80,9 +114,9 @@
 
 static void nubus_advance(unsigned char **ptr, int len, int map)
 {
-	unsigned char *p=*ptr;
+	unsigned char *p = *ptr;
 	if(len>65536)
-		printk("advance of %d!\n", len);
+		printk(KERN_ERR "advance of 0x%08x!\n", len);
 	while(len)
 	{
 		while(not_useful(p,map))
@@ -90,29 +124,32 @@
 			p++;
 		len--;
 	}
-	*ptr=p;
+	*ptr = p;
 }
 
-/*
- *	24bit signed offset to 32bit
- */
- 
-static unsigned long nubus_expand32(unsigned long foo)
+static void nubus_move(unsigned char **ptr, int len, int map)
 {
-	if(foo&0x00800000)	/* 24bit negative */
-		foo|=0xFF000000;
-	return foo;
+	if(len > 0)
+		nubus_advance(ptr, len, map);
+	else if(len < 0)
+		nubus_rewind(ptr, -len, map);
 }
 
-static void nubus_move(unsigned char **ptr, int len, int map)
+/* Now, functions to read the sResource tree */
+
+/* Each sResource entry consists of a 1-byte ID and a 3-byte data
+   field.  If that data field contains an offset, then obviously we
+   have to expand it from a 24-bit signed number to a 32-bit signed
+   number. */
+
+extern inline long nubus_expand32(long foo)
 {
-	if(len>0)
-		nubus_advance(ptr,len,map);
-	else if(len<0)
-		nubus_rewind(ptr,-len,map);
+	if(foo & 0x00800000)	/* 24bit negative */
+		foo |= 0xFF000000;
+	return foo;
 }
 
-static void *nubus_rom_addr(int slot)
+extern inline void *nubus_rom_addr(int slot)
 {	
 	/*
 	 *	Returns the first byte after the card. We then walk
@@ -121,472 +158,734 @@
 	return (void *)(0xF1000000+(slot<<24));
 }
 
-void nubus_memcpy(int slot, void *to, unsigned char *p, int len)
+static unsigned char *nubus_dirptr(const struct nubus_dirent *nd)
 {
-	unsigned char *t=(unsigned char *)to;
+	unsigned char *p = nd->base;
+	/* Essentially, just step over the bytelanes using whatever
+	   offset we might have found */
+	nubus_move(&p, nubus_expand32(nd->data), nd->mask);
+	/* And return the value */
+	return p;
+}
+
+/* These two are for pulling resource data blocks (i.e. stuff that's
+   pointed to with offsets) out of the card ROM. */
+
+void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent* dirent,
+			int len)
+{
+	unsigned char *t = (unsigned char *)dest;
+	unsigned char *p = nubus_dirptr(dirent);
 	while(len)
 	{
-		*t++=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+		*t++ = nubus_get_rom(&p, 1, dirent->mask);
 		len--;
 	}
 }
 
-void nubus_strncpy(int slot, void *to, unsigned char *p, int len)
+void nubus_get_rsrc_str(void *dest, const struct nubus_dirent* dirent,
+			int len)
 {
-	unsigned char *t=(unsigned char *)to;
+	unsigned char *t=(unsigned char *)dest;
+	unsigned char *p = nubus_dirptr(dirent);
 	while(len)
 	{
-		*t=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+		*t = nubus_get_rom(&p, 1, dirent->mask);
 		if(!*t++)
 			break;
 		len--;
 	}
 }
 
-
-
-	
-unsigned char *nubus_dirptr(struct nubus_dirent *nd)
+int nubus_get_root_dir(const struct nubus_board* board,
+		       struct nubus_dir* dir)
 {
-	unsigned char *p=(unsigned char *)(nd->base);
-	
-	nubus_move(&p, nubus_expand32(nd->value), nd->mask);
-	return p;
+	dir->ptr = dir->base = board->directory;
+	dir->done = 0;
+	dir->mask = board->lanes;
+	return 0;
 }
 
-	
-struct nubus_dir *nubus_openrootdir(int slot)
-{
-	static struct nubus_dir nbdir;
-	unsigned char *rp=nubus_rom_addr(slot);
-	
-	nubus_rewind(&rp,20, nubus_slots[slot].slot_lanes);
-
-	nubus_move(&rp, nubus_expand32(nubus_slots[slot].slot_directory),
-		nubus_slots[slot].slot_lanes);
-		
-	nbdir.base=rp;
-	nbdir.length=nubus_slots[slot].slot_dlength;
-	nbdir.count=0;
-	nbdir.mask=nubus_slots[slot].slot_lanes;
-	return &nbdir;
+/* This is a slyly renamed version of the above */
+int nubus_get_func_dir(const struct nubus_dev* dev,
+		       struct nubus_dir* dir)
+{
+	dir->ptr = dir->base = dev->directory;
+	dir->done = 0;
+	dir->mask = dev->board->lanes;
+	return 0;
 }
 
-struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d)
+int nubus_get_board_dir(const struct nubus_board* board,
+			struct nubus_dir* dir)
 {
-	static struct nubus_dir nbdir;
-	unsigned char *rp=nubus_dirptr(d);
-	nbdir.base=rp;
-	nbdir.length=99999;/*slots[i].slot_dlength;*/
-	nbdir.count=0;
-	nbdir.mask=d->mask;
-	return &nbdir;
+	struct nubus_dirent ent;
+	
+	dir->ptr = dir->base = board->directory;
+	dir->done = 0;
+	dir->mask = board->lanes;
+
+	/* Now dereference it (the first directory is always the board
+	   directory) */
+	if (nubus_readdir(dir, &ent) == -1)
+		return -1;
+	if (nubus_get_subdir(&ent, dir) == -1)
+		return -1;
+	return 0;
 }
 
-void nubus_closedir(struct nubus_dir *nd)
+int nubus_get_subdir(const struct nubus_dirent *ent,
+		     struct nubus_dir *dir)
 {
-	;
+	dir->ptr = dir->base = nubus_dirptr(ent);
+	dir->done = 0;
+	dir->mask = ent->mask;
+	return 0;
 }
 
-struct nubus_dirent *nubus_readdir(struct nubus_dir *nd)
+int nubus_readdir(struct nubus_dir *nd, struct nubus_dirent *ent)
 {
 	u32 resid;
-	u8 rescode;
-	static struct nubus_dirent d;
-	
-	if(nd->count==nd->length)
-		return NULL;
+	if (nd->done)
+		return -1;
 
-	d.base=(unsigned long)nd->base;
-		
-	resid=nubus_get_rom(&nd->base, 4, nd->mask);
-	nd->count++;
-	rescode=resid>>24;
-	if(rescode==0xFF)
-	{
-		nd->count=nd->length;
-		return NULL;
-	}
-	d.type=rescode;
-	d.value=resid&0xFFFFFF;
-	d.mask=nd->mask;
-	return &d;
-}
+	/* Do this first, otherwise nubus_rewind & co are off by 4 */
+	ent->base = nd->ptr;
 
-/*
- *	MAC video handling irritations
- */
+	/* This moves nd->ptr forward */
+	resid = nubus_get_rom(&nd->ptr, 4, nd->mask);
 
-static unsigned char nubus_vid_byte[16];
-static unsigned long nubus_vid_offset[16];
+	/* EOL marker, as per the Apple docs */
+	if((resid&0xff000000) == 0xff000000)
+	{
+		/* Mark it as done */
+		nd->done = 1;
+		return -1;
+	}
 
-static void nubus_irqsplat(int slot, void *dev_id, struct pt_regs *regs)
-{
-	unsigned char *p=((unsigned char *)nubus_slot_addr(slot))+
-			nubus_vid_offset[slot];
-	*p=nubus_vid_byte[slot];
+	/* First byte is the resource ID */
+	ent->type  = resid >> 24;
+	/* Low 3 bytes might contain data (or might not) */
+	ent->data = resid & 0xffffff;
+	ent->mask  = nd->mask;
+	return 0;
 }
 
-static int nubus_add_irqsplatter(int slot, unsigned long ptr, unsigned char v)
+int nubus_rewinddir(struct nubus_dir* dir)
 {
-	nubus_vid_byte[slot]=v;
-	nubus_vid_offset[slot]=ptr;
-	nubus_request_irq(slot, NULL, nubus_irqsplat);
+	dir->ptr = dir->base;
+	dir->done = 0;
 	return 0;
 }
- 
-void nubus_video_shutup(int slot, struct nubus_type *nt)
-{
-	if(nt->category!=3 /* Display */ || nt->type!=1 /* Video */
-		|| nt->DrSW!=1 /* Quickdraw device */)
-		return;
-	switch(nt->DrHW)
-	{
-		/*
-		 *	Toby and MacII Hires cards. These behave in a MacII
-		 *	anyway but not on an RBV box
-		 */
-		case 0x0001:
-		case 0x0013:
-			nubus_add_irqsplatter(slot, 0xA0000, 0);
-			break;
-		/*
-		 *	Apple workstation video card.
-		 */
-		case 0x0006:
-			nubus_add_irqsplatter(slot, 0xA00000, 0);
-			break;
-		/*
-		 *	Futura cards
-		 */
-		case 0x0417:
-		case 0x042F:
-			nubus_add_irqsplatter(slot, 0xF05000, 0x80);
-			break;
-			
-		/*
-		 *	Fingers crossed 8)
-		 *	
-		 *	If you have another card and an RBV based mac you'll
-		 *	almost certainly have to add it here to make it work.
-		 */
-		
-		default:
-			break;
+
+/* Driver interface functions, more or less like in pci.c */
+
+struct nubus_dev*
+nubus_find_device(unsigned short category,
+		  unsigned short type,
+		  unsigned short dr_hw,
+		  unsigned short dr_sw,
+		  const struct nubus_dev* from)
+{
+	struct nubus_dev* itor =
+		from ? from->next : nubus_devices;
+
+	while (itor) {
+		if (itor->category == category
+		    && itor->type == type
+		    && itor->dr_hw == dr_hw
+		    && itor->dr_sw == dr_sw)
+			return itor;
+		itor = itor->next;
 	}
+	return NULL;
 }
 
-/*
- *	Device list
- */
+struct nubus_dev*
+nubus_find_type(unsigned short category,
+		unsigned short type,
+		const struct nubus_dev* from)
+{
+	struct nubus_dev* itor =
+		from ? from->next : nubus_devices;
+
+	while (itor) {
+		if (itor->category == category
+		    && itor->type == type)
+			return itor;
+		itor = itor->next;
+	}
+	return NULL;
+}
 
-static struct nubus_device_specifier *nubus_device_list=NULL;
- 
-void register_nubus_device(struct nubus_device_specifier *d)
-{
-	d->next=nubus_device_list;
-	nubus_device_list=d;
+struct nubus_dev*
+nubus_find_slot(unsigned int slot,
+		const struct nubus_dev* from)
+{
+	struct nubus_dev* itor =
+		from ? from->next : nubus_devices;
+	
+	while (itor) {
+		if (itor->board->slot == slot)
+			return itor;
+		itor = itor->next;
+	}
+	return NULL;
 }
 
-void unregister_nubus_device(struct nubus_device_specifier *nb)
-{
-	struct nubus_device_specifier **t=&nubus_device_list;
-	while(*t!=nb && *t)
-		t=&((*t)->next);
-	*t=nb->next;
+int
+nubus_find_rsrc(struct nubus_dir* dir, unsigned char rsrc_type,
+		struct nubus_dirent* ent)
+{
+	while (nubus_readdir(dir, ent) != -1) {
+		if (ent->type == rsrc_type)
+			return 0;
+	}	
+	return -1;
+}
+
+/* Initialization functions - decide which slots contain stuff worth
+   looking at, and print out lots and lots of information from the
+   resource blocks. */
+
+/* FIXME: A lot of this stuff will eventually be useful after
+   initializaton, for intelligently probing Ethernet and video chips,
+   among other things.  The rest of it should go in the /proc code.
+   For now, we just use it to give verbose boot logs. */
+
+static int __init nubus_show_display_resource(struct nubus_dev* dev,
+					      const struct nubus_dirent* ent)
+{
+	switch (ent->type) {
+	case NUBUS_RESID_GAMMADIR:
+		printk(KERN_INFO "    gamma directory offset: 0x%06x\n", ent->data);
+		break;
+	case 0x0080 ... 0x0085:
+		printk(KERN_INFO "    mode %02X info offset: 0x%06x\n",
+		       ent->type, ent->data);
+		break;
+	default:
+		printk(KERN_INFO "    unknown resource %02X, data 0x%06x\n",
+		       ent->type, ent->data);
+	}
+	return 0;
 }
 
-static struct nubus_device_specifier *find_nubus_device(int slot, struct nubus_type *nt)
+static int __init nubus_show_network_resource(struct nubus_dev* dev,
+					      const struct nubus_dirent* ent)
 {
-	struct nubus_device_specifier *t=nubus_device_list; 
-	while(t!=NULL)
+	switch (ent->type) {
+	case NUBUS_RESID_MAC_ADDRESS:
 	{
-		if(t->setup(t,slot, nt)==0)
-			return t;
-		t=t->next;
+		char addr[6];
+		int i;
+		
+		nubus_get_rsrc_mem(addr, ent, 6);
+		printk(KERN_INFO "    MAC address: ");
+		for (i = 0; i < 6; i++)
+			printk("%02x%s", addr[i] & 0xff,
+			       i == 5 ? "" : ":");
+		printk("\n");
+		break;
+	}
+	default:
+		printk(KERN_INFO "    unknown resource %02X, data 0x%06x\n",
+		       ent->type, ent->data);
 	}
-	printk("No driver for device [%d %d %d %d]\n",
-		nt->category, nt->type, nt->DrHW, nt->DrSW);
-	return NULL;
+	return 0;
 }
 
-/*
- *	Probe a nubus slot
- */
+static int __init nubus_show_cpu_resource(struct nubus_dev* dev,
+					  const struct nubus_dirent* ent)
+{
+	switch (ent->type) {
+	case NUBUS_RESID_MEMINFO:
+	{
+		unsigned long meminfo[2];
+		nubus_get_rsrc_mem(&meminfo, ent, 8);
+		printk(KERN_INFO "    memory: [ 0x%08lx 0x%08lx ]\n",
+		       meminfo[0], meminfo[1]);
+		break;
+	}
+	case NUBUS_RESID_ROMINFO:
+	{
+		unsigned long rominfo[2];
+		nubus_get_rsrc_mem(&rominfo, ent, 8);
+		printk(KERN_INFO "    ROM:    [ 0x%08lx 0x%08lx ]\n",
+		       rominfo[0], rominfo[1]);
+		break;
+	}
+	default:
+		printk(KERN_INFO "    unknown resource %02X, data 0x%06x\n",
+		       ent->type, ent->data);
+	}
+	return 0;
+}
 
-void nubus_probe_slot(int slot, int mode)
+static int __init nubus_show_private_resource(struct nubus_dev* dev,
+					      const struct nubus_dirent* ent)
 {
-	unsigned char *rp;
-	unsigned char dp;
-	int lanes;
-	int i;
-	unsigned long dpat;
-	struct nubus_dir *dir;
-	struct nubus_dirent *nd;
-	struct nubus_type type_info;
+	switch (dev->category) {
+	case NUBUS_CAT_DISPLAY:
+		nubus_show_display_resource(dev, ent);
+		break;
+	case NUBUS_CAT_NETWORK:
+		nubus_show_network_resource(dev, ent);
+		break;
+	case NUBUS_CAT_CPU:
+		nubus_show_cpu_resource(dev, ent);
+		break;
+	default:
+		printk(KERN_INFO "    unknown resource %02X, data 0x%06x\n",
+		       ent->type, ent->data);
+	}
+	return 0;
+}
 
-	/*
-	 *	Ok see whats cooking in the bytelanes
-	 */
+static struct nubus_dev* __init
+nubus_get_functional_resource(struct nubus_board* board,
+			      int slot,
+			      const struct nubus_dirent* parent)
+{
+	struct nubus_dir    dir;
+	struct nubus_dirent ent;
+	struct nubus_dev*   dev;
+	
+	printk(KERN_INFO "  Function 0x%02x:\n", parent->type);
+	nubus_get_subdir(parent, &dir);
+
+	printk(KERN_DEBUG "nubus_get_functional_resource: parent is 0x%p, dir is 0x%p\n",
+	       parent->base, dir.base);
+
+	/* Actually we should probably panic if this fails */
+	if ((dev = kmalloc(sizeof(*dev), GFP_ATOMIC)) == NULL)
+		return NULL;	
+	memset(dev, 0, sizeof(*dev));
+	dev->resid = parent->type;
+	dev->directory = dir.base;
+	dev->board = board;
 	
-	rp=nubus_rom_addr(slot);
-	
-	for(i=4;i;i--)
+	while (nubus_readdir(&dir, &ent) != -1)
 	{
-		rp--;
-		
-		if(!hwreg_present(rp))
-			continue;
-			
-		dp=*rp;
-	
-		if(dp==0)
-			continue;
-		
-		/*
-		 *	Valid ?
-		 */
-		 
-		if((((dp>>4)^dp)&0x0F)!=0x0F)
-			continue;
-			
-		if((dp&0x0F) >= 1<<i)
-			continue;
-			
-		/*
-		 *	Looks promising
-		 */
-		
-		nubus_slots[slot].slot_flags|=NUBUS_DEVICE_PRESENT;
-		lanes=dp;
-
-		if (mode==0)
-			printk("nubus%c: ",
-				"0123456789abcdef"[slot]);
-		
-		
-		/*
-		 *	Time to dig deeper. Find the ROM base
-		 *	and read it
-		 */
-		 
-		rp=nubus_rom_addr(slot); 
-		
-		/*
-		 *	Now to make this more fun the ROM is only visible
-		 *	on its bytelanes - that is smeared across the address
-		 *	space.
-		 */
-		
-		nubus_rewind(&rp,20,lanes);
-		
-		nubus_slots[slot].slot_directory=	nubus_get_rom(&rp,4,lanes);
-		nubus_slots[slot].slot_dlength	=	nubus_get_rom(&rp,4,lanes);
-		nubus_slots[slot].slot_crc	=	nubus_get_rom(&rp,4,lanes);
-		nubus_slots[slot].slot_rev	=	nubus_get_rom(&rp,1,lanes);
-		nubus_slots[slot].slot_format	=	nubus_get_rom(&rp,1,lanes);
-		nubus_slots[slot].slot_lanes	=	lanes;
-		
-		dpat=nubus_get_rom(&rp,4,lanes);
-		
-		/*
-		 *	Ok now check what we got
-		 */
-		
-		if(!(nubus_slots[slot].slot_directory&0x00FF0000))
-			printk("Dodgy doffset ??\n");
-		if(dpat!=0x5A932BC7)
-			printk("Wrong test pattern %lx\n",dpat);
-		
-		/*
-		 *	I wonder how the CRC is meant to work -
-		 *		any takers ?
-		 */
-		
-		
-		/*
-		 *	Now parse the directories on the card
-		 */
-		 
-		
-		dir=nubus_openrootdir(slot);
-		
-		/*
-		 *	Find the board resource
-		 */
-		 
-		while((nd=nubus_readdir(dir))!=NULL)
+		switch(ent.type)
+		{
+		case NUBUS_RESID_TYPE:
 		{
-			/*
-			 *	This ought to be 1. 1 doesn't work, 0x80
-			 *	does. Seems the Apple docs are wrong.
-			 */
-			if(nd->type==0x80/*RES_ID_BOARD_DIR*/)
-				break;
+			unsigned short nbtdata[4];
+			nubus_get_rsrc_mem(nbtdata, &ent, 8);
+			dev->category = nbtdata[0];
+			dev->type     = nbtdata[1];
+			dev->dr_sw    = nbtdata[2];
+			dev->dr_hw    = nbtdata[3];
+			printk(KERN_INFO "    type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
+			       nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
+			break;
 		}
-		
-		nubus_closedir(dir);
-		
-		if(nd==NULL)
+		case NUBUS_RESID_NAME:
 		{
-			printk("board resource not found!\n");
-			return;
+			nubus_get_rsrc_str(dev->name, &ent, 64);
+			printk(KERN_INFO "    name: %s\n", dev->name);
+			break;
 		}
-		
-		dir=nubus_opensubdir(nd);
-				
-		/*
-		 *	Walk the board resource
-		 */
-		 
-		while((nd=nubus_readdir(dir))!=NULL)
+		case NUBUS_RESID_DRVRDIR:
 		{
-			switch(nd->type)
-			{
-				case RES_ID_TYPE:
-				{
-					unsigned short nbtdata[4];
-					nubus_memcpy(slot, nbtdata, nubus_dirptr(nd), 8);
-					type_info.category=nbtdata[0];
-					type_info.type=nbtdata[1];
-					type_info.DrHW=nbtdata[2];
-					type_info.DrSW=nbtdata[3];
-					break;
-				}
-				case RES_ID_NAME:
-					nubus_strncpy(slot, nubus_slots[slot].slot_cardname,nubus_dirptr(nd),64);
-					break;
-				default:
-					;
-			}
+			/* MacOS driver.  If we were NetBSD we might
+			   use this :-) */
+			struct nubus_dir drvr_dir;
+			struct nubus_dirent drvr_ent;
+			nubus_get_subdir(&ent, &drvr_dir);
+			nubus_readdir(&drvr_dir, &drvr_ent);
+			dev->driver = nubus_dirptr(&drvr_ent);
+			printk(KERN_INFO "    driver at: 0x%p\n",
+			       dev->driver);
+			break;
 		}
-		
-		nubus_closedir(dir);
-
-		/*
-		 *	Attempt to bind a driver to this slot
-		 */
-		
-		if (mode==0) {
-			printk("%s\n",
-				nubus_slots[slot].slot_cardname);
-			find_nubus_device(slot,&type_info);
+		case NUBUS_RESID_MINOR_BASEOS:
+			/* We will need this in order to support
+			   multiple framebuffers.  It might be handy
+			   for Ethernet as well */
+			nubus_get_rsrc_mem(&dev->iobase, &ent, 4);
+			printk(KERN_INFO "    memory offset: 0x%08lx\n",
+			       dev->iobase);
+			break;
+		case NUBUS_RESID_MINOR_LENGTH:
+			/* Ditto */
+			nubus_get_rsrc_mem(&dev->iosize, &ent, 4);
+			printk(KERN_INFO "    memory length: 0x%08lx\n",
+			       dev->iosize);
+			break;			
+		case NUBUS_RESID_FLAGS:
+			dev->flags = ent.data;
+			printk(KERN_INFO "    flags: 0x%06x\n", dev->flags);
+			break;
+		case NUBUS_RESID_HWDEVID:
+			dev->hwdevid = ent.data;
+			printk(KERN_INFO "    hwdevid: 0x%06x\n", dev->hwdevid);
+			break;
+		default:
+			/* Local/Private resources have their own
+			   function */
+			nubus_show_private_resource(dev, &ent);
 		}
-		if (mode==1)
-			nubus_video_shutup(slot, &type_info);
+	}
+		
+	return dev;
+}
 
-		return;
+/* This is cool. */
+static int __init nubus_get_vidnames(struct nubus_board* board,
+				     const struct nubus_dirent* parent)
+{
+	struct nubus_dir    dir;
+	struct nubus_dirent ent;
+	/* FIXME: obviously we want to put this in a header file soon */
+	struct vidmode {
+		u32 size;
+		/* Don't know what this is yet */
+		u16 id;
+		/* Longest one I've seen so far is 26 characters */
+		char name[32];
+	};
+
+	printk(KERN_INFO "    video modes supported:\n");
+	nubus_get_subdir(parent, &dir);
+	printk(KERN_DEBUG "nubus_get_vidnames: parent is 0x%p, dir is 0x%p\n",
+	       parent->base, dir.base);
+
+	while(nubus_readdir(&dir, &ent) != -1)
+	{
+		struct vidmode mode;
+		u32 size;
+
+		/* First get the length */
+		nubus_get_rsrc_mem(&size, &ent, 4);
+		
+		/* Now clobber the whole thing */
+		if (size > sizeof(mode) - 1)
+			size = sizeof(mode) - 1;
+		memset(&mode, sizeof(mode), 0);
+		nubus_get_rsrc_mem(&mode, &ent, size);
+		printk (KERN_INFO "      %02X: (%02X) %s\n", ent.type,
+			mode.id, mode.name);
 	}
+	return 0;
 }
 
+/* This is *really* cool. */
+static int __init nubus_get_icon(struct nubus_board* board,
+				 const struct nubus_dirent* ent)
+{
+	/* Should be 32x32 if my memory serves me correctly */
+	unsigned char icon[128];
+	int x, y;
+	
+	nubus_get_rsrc_mem(&icon, ent, 128);
+	printk(KERN_INFO "    icon:\n");
+
+	/* We should actually plot these somewhere in the framebuffer
+	   init.  This is just to demonstrate that they do, in fact,
+	   exist */
+	for (y = 0; y < 32; y++) {
+		printk(KERN_INFO "      ");
+		for (x = 0; x < 32; x++) {
+			if (icon[y*4 + x/8]
+			    & (0x80 >> (x%8)))
+				printk("*");
+			else
+				printk(" ");
+		}
+		printk("\n");
+	}
+	return 0;
+}
 
-void nubus_probe_bus(void)
+static int __init nubus_get_vendorinfo(struct nubus_board* board,
+				       const struct nubus_dirent* parent)
 {
-	int i;
-	for(i=9;i<15;i++)
-	{
-		/* printk("nubus: probing slot %d !\n", i); */
-		nubus_probe_slot(i, 0);
+	struct nubus_dir    dir;
+	struct nubus_dirent ent;
+	static char* vendor_fields[6] = {"ID", "serial", "revision",
+					 "part", "date", "unknown field"};
+
+	printk(KERN_INFO "    vendor info:\n");
+	nubus_get_subdir(parent, &dir);
+	printk(KERN_DEBUG "nubus_get_vendorinfo: parent is 0x%p, dir is 0x%p\n",
+	       parent->base, dir.base);
+
+	while(nubus_readdir(&dir, &ent) != -1)
+	{
+		char name[64];
+		
+		/* These are all strings, we think */
+		nubus_get_rsrc_str(name, &ent, 64);
+		if (ent.type > 5)
+			ent.type = 5;
+		printk(KERN_INFO "    %s: %s\n",
+		       vendor_fields[ent.type-1], name);
 	}
+	return 0;
 }
 
-/*
- *	RBV machines have level triggered video interrupts, and a VIA
- *	emulation that doesn't always seem to include being able to disable 
- *	an interrupt. Totally lusing hardware. Before we can init irq's we
- *	have to install a handler to shut the bloody things up.
- */
-
-void nubus_sweep_video(void)
+static int __init nubus_get_board_resource(struct nubus_board* board, int slot,
+					   const struct nubus_dirent* parent)
 {
-	int i;
-	return; /* XXX why ?? */
-	for(i=9;i<15;i++)
+	struct nubus_dir    dir;
+	struct nubus_dirent ent;
+	
+	nubus_get_subdir(parent, &dir);
+	printk(KERN_DEBUG "nubus_get_board_resource: parent is 0x%p, dir is 0x%p\n",
+	       parent->base, dir.base);
+
+	while(nubus_readdir(&dir, &ent) != -1)
 	{
-		nubus_probe_slot(i,1);
+		switch (ent.type) {
+		case NUBUS_RESID_TYPE:
+		{
+			unsigned short nbtdata[4];
+			/* This type is always the same, and is not
+			   useful except insofar as it tells us that
+			   we really are looking at a board resource. */
+			nubus_get_rsrc_mem(nbtdata, &ent, 8);
+			printk(KERN_INFO "    type: [cat 0x%x type 0x%x hw 0x%x sw 0x%x]\n",
+			       nbtdata[0], nbtdata[1], nbtdata[2],
+			       nbtdata[3]);
+			if (nbtdata[0] != 1 || nbtdata[1] != 0 ||
+			    nbtdata[2] != 0 || nbtdata[3] != 0)
+				printk(KERN_ERR "this sResource is not a board resource!\n");
+			break;
+		}
+		case NUBUS_RESID_NAME:
+			nubus_get_rsrc_str(board->name, &ent, 64);
+			printk(KERN_INFO "    name: %s\n", board->name);
+			break;
+		case NUBUS_RESID_ICON:
+			nubus_get_icon(board, &ent);
+			break;
+		case NUBUS_RESID_BOARDID:
+			printk(KERN_INFO "    board id: 0x%x\n", ent.data);
+			break;
+		case NUBUS_RESID_PRIMARYINIT:
+			printk(KERN_INFO "    primary init offset: 0x%06x\n", ent.data);
+			break;
+		case NUBUS_RESID_VENDORINFO:
+			nubus_get_vendorinfo(board, &ent);
+			break;
+		case NUBUS_RESID_FLAGS:
+			printk(KERN_INFO "    flags: 0x%06x\n", ent.data);
+			break;
+		case NUBUS_RESID_HWDEVID:
+			printk(KERN_INFO "    hwdevid: 0x%06x\n", ent.data);
+			break;
+		case NUBUS_RESID_SECONDINIT:
+			printk(KERN_INFO "    secondary init offset: 0x%06x\n", ent.data);
+			break;
+			/* WTF isn't this in the functional resources? */ 
+		case NUBUS_RESID_VIDNAMES:
+			nubus_get_vidnames(board, &ent);
+			break;
+			/* Same goes for this */
+		case NUBUS_RESID_VIDMODES:
+			printk(KERN_INFO "    video mode parameter directory offset: 0x%06x\n",
+			       ent.data);
+			break;			
+		default:
+			printk(KERN_INFO "    unknown resource %02X, data 0x%06x\n",
+			       ent.type, ent.data);
+		}
 	}
+	return 0;
 }
 
-/*
- *	Support functions
- */
- 
-int nubus_ethernet_addr(int slot, unsigned char *addr)
+/* Add a board (might be many devices) to the list */
+static struct nubus_board* __init nubus_add_board(int slot, int bytelanes)
 {
-	struct nubus_dir *nb;
-	struct nubus_dirent *d;
-	int ng=-ENOENT;
-		
-	nb=nubus_openrootdir(slot);
+	struct nubus_board* board;
+	struct nubus_board** boardp;
+
+	unsigned char *rp;
+	unsigned long dpat;
+	struct nubus_dir dir;
+	struct nubus_dirent ent;
+
+	/* Move to the start of the format block */
+	rp = nubus_rom_addr(slot);		
+	nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes);
+
+	/* Actually we should probably panic if this fails */
+	if ((board = kmalloc(sizeof(*board), GFP_ATOMIC)) == NULL)
+		return NULL;	
+	memset(board, 0, sizeof(*board));
+	board->fblock = rp;
+
+	/* Dump the format block for debugging purposes */
+	if (console_loglevel >= 10) {
+		int i;
+		printk(KERN_DEBUG "Slot %X, format block at 0x%p\n",
+		       slot, rp);
+		printk(KERN_DEBUG "Format block: ");
+		for (i = 0; i < FORMAT_BLOCK_SIZE; i += 4) {
+			unsigned short foo, bar;
+			foo = nubus_get_rom(&rp, 2, bytelanes);
+			bar = nubus_get_rom(&rp, 2, bytelanes);
+			printk("%04x %04x  ", foo, bar);
+		}
+		printk("\n");
+		rp = board->fblock;
+	}
 	
-	if(nb==NULL)
-		return -ENOENT;
+	board->slot = slot;
+	board->slot_addr = (unsigned long) nubus_slot_addr(slot);
+	board->doffset = nubus_get_rom(&rp, 4, bytelanes);
+	/* rom_length is *supposed* to be the total length of the
+	 * ROM.  In practice it is the "amount of ROM used to compute
+	 * the CRC."  So some jokers decide to set it to zero and
+	 * set the crc to zero so they don't have to do any math.
+	 * See the Performa 460 ROM, for example.  Those Apple "engineers".
+	 */
+	board->rom_length = nubus_get_rom(&rp, 4, bytelanes);
+	board->crc = nubus_get_rom(&rp, 4, bytelanes);
+	board->rev = nubus_get_rom(&rp, 1, bytelanes);
+	board->format = nubus_get_rom(&rp,1, bytelanes);
+	board->lanes = bytelanes;
+
+	/* Directory offset should be small and negative... */
+	if(!(board->doffset & 0x00FF0000))
+		printk(KERN_WARNING "Dodgy doffset!\n");
+	dpat = nubus_get_rom(&rp, 4, bytelanes);
+	if(dpat != NUBUS_TEST_PATTERN)
+		printk(KERN_WARNING "Wrong test pattern %08lx!\n", dpat);
 		
-	while((d=nubus_readdir(nb))!=NULL)
-	{
-		if(d->type==0x80)	/* First private resource */
-			break;
+	/*
+	 *	I wonder how the CRC is meant to work -
+	 *		any takers ?
+	 * CSA: According to MAC docs, not all cards pass the CRC anyway,
+	 * since the initial Macintosh ROM releases skipped the check.
+	 */
+
+	/* Set up the directory pointer */
+	board->directory = board->fblock;
+	nubus_move(&board->directory, nubus_expand32(board->doffset), board->lanes);
+
+	nubus_get_root_dir(board, &dir);	
+
+	/* We're ready to rock */
+	printk(KERN_INFO "Slot %X:\n", slot);
+
+	/* Each slot should have one board resource and any number of
+	   functional resources.  So we'll fill in some fields in the
+	   struct nubus_board from the board resource, then walk down
+	   the list of functional resources, spinning out a nubus_dev
+	   for each of them. */
+	if (nubus_readdir(&dir, &ent) == -1) {
+		/* We can't have this! */
+		printk(KERN_ERR "Board resource not found!\n");
+		return NULL;
+	} else {
+		printk(KERN_INFO "  Board resource:\n");
+		nubus_get_board_resource(board, slot, &ent);
 	}
-	if(d==NULL)
-		return -ENOENT;
-	
-	nb=nubus_opensubdir(d);
+
+	while (nubus_readdir(&dir, &ent) != -1) {
+		struct nubus_dev*  dev;
+		struct nubus_dev** devp;
+		dev = nubus_get_functional_resource(board, slot, &ent);
+		if (dev == NULL)
+			continue;
+
+		/* We zeroed this out above */
+		if (board->first_dev == NULL)
+			board->first_dev = dev;
+		
+		/* Put it on the global NuBus device chain. Keep entries in order. */
+		for (devp=&nubus_devices; *devp!=NULL; devp=&((*devp)->next))
+			/* spin */;
+		*devp = dev;
+		dev->next = NULL;		
+	}
+
+	/* Put it on the global NuBus board chain. Keep entries in order. */
+	for (boardp=&nubus_boards; *boardp!=NULL; boardp=&((*boardp)->next))
+		/* spin */;
+	*boardp = board;
+	board->next = NULL;
 	
-	while((d=nubus_readdir(nb))!=NULL)
+	return board;
+}
+
+void __init nubus_probe_slot(int slot)
+{
+	unsigned char dp;
+	unsigned char* rp;
+	int i;
+
+	rp = nubus_rom_addr(slot);	
+	for(i = 4; i; i--)
 	{
-		if(d->type==0x80)	/* First private field is the mac */
-		{
-			int i;
-			nubus_memcpy(slot, addr, nubus_dirptr(d), 6);
-/*			printk("d.base=%lX, d.value=%lX\n",
-				d->base,d->value);
-			memcpy(addr,"\xC0\xC1\xC2\xC3\xC4\xC5",6);*/
-			printk("MAC address: ");
-			for(i=0;i<6;i++)
-			{
-				printk("%s%02X", i?":":"", addr[i]);
-			}
-			ng=0;
-			break;
-		}
-		else
-			printk("ID=%d val=%x\n",
-				d->type, d->value);
+		unsigned long flags;
+		int card_present;
+
+		rp--;
+		save_flags(flags);
+		cli();
+		card_present = hwreg_present(rp);
+		restore_flags(flags);
+	       
+		if (!card_present)
+			continue;
+
+		dp = *rp;
+		if(dp == 0)
+			continue;
+
+		/* The last byte of the format block consists of two
+		   nybbles which are "mirror images" of each other.
+		   These show us the valid bytelanes */
+		if ((((dp>>4) ^ dp) & 0x0F) != 0x0F)
+			continue;
+		/* Check that this value is actually *on* one of the
+		   bytelanes it claims are valid! */
+		if ((dp & 0x0F) >= (1<<i))
+			continue;
+
+		/* Looks promising.  Let's put it on the list. */
+		nubus_add_board(slot, dp);
+
+		return;
 	}
-	return ng;
 }
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS)
 
-/*
- *	/proc for Nubus devices
- */
- 
-static int sprint_nubus_config(int slot, char *ptr, int len)
+/* /proc/nubus stuff */
+
+static int sprint_nubus_board(struct nubus_board* board, char* ptr, int len)
 {
-	if(len<150)
+	if(len < 100)
 		return -1;
-	sprintf(ptr, "Device: %s %s\n", nubus_slots[slot].slot_cardname,
-		(nubus_slots[slot].slot_flags&NUBUS_DEVICE_ACTIVE)?
-			"[active]":"[unused]");
+	
+	sprintf(ptr, "Slot %X: %s\n",
+		board->slot, board->name);
+	
 	return strlen(ptr);
 }
 
+/* We're going to have to be a bit more sophisticated about this, I
+   think, because it doesn't really seem to work right when you do a
+   full listing of boards and devices */
 int get_nubus_list(char *buf)
 {
 	int nprinted, len, size;
-	int slot;
+	struct nubus_board* board;
 #define MSG "\nwarning: page-size limit reached!\n"
 
 	/* reserve same for truncation warning message: */
 	size  = PAGE_SIZE - (strlen(MSG) + 1);
-	len   = sprintf(buf, "Nubus devices found:\n");
+	len   = sprintf(buf, "Nubus boards found:\n");
 
-	for (slot=0; slot< 16; slot++) 
+	/* Walk the list of NuBus boards */
+	for (board = nubus_boards; board != NULL; board = board->next)
 	{
-		if(!(nubus_slots[slot].slot_flags&NUBUS_DEVICE_PRESENT))
-			continue;
-		nprinted = sprint_nubus_config(slot, buf + len, size - len);
+		nprinted = sprint_nubus_board(board, buf + len, size - len);
 		if (nprinted < 0) {
 			return len + sprintf(buf + len, MSG);
 		}
@@ -595,46 +894,42 @@
 	return len;
 }
 
-static struct proc_dir_entry proc_nubus = {
+static struct proc_dir_entry proc_old_nubus = {
 	PROC_NUBUS, 5, "nubus",
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_array_inode_operations
 };
-#endif
+#endif /* CONFIG_PROC_FS */
 
-void nubus_init(void)
+void __init nubus_scan_bus(void)
 {
-	/* 
-	 *	Register cards
-	 */
-#ifdef CONFIG_DAYNAPORT
-	extern struct nubus_device_specifier nubus_8390;	
-#endif
+	int slot;
+	for(slot = 9; slot < 15; slot++)
+	{
+		nubus_probe_slot(slot);
+	}
+}
 
+void __init nubus_init(void)
+{
 	if (!MACH_IS_MAC) 
 		return;
 
-#ifdef LCIII_WEIRDNESS
-	if (macintosh_config->ident == MAC_MODEL_LCIII) {
-	        printk("nubus init: LCIII has no nubus!\n");
-		return;
+	/* Initialize the NuBus interrupts */
+	if (oss_present) {
+		oss_nubus_init();
+	} else {
+		via_nubus_init();
 	}
-#endif
 
-#ifdef CONFIG_DAYNAPORT
-	register_nubus_device(&nubus_8390);
-#endif
+	/* And probe */
+	printk("NuBus: Scanning NuBus slots.\n");
+	nubus_devices = NULL;
+	nubus_boards  = NULL;
+	nubus_scan_bus();
 
-	/*
-	 *	And probe
-	 */
-	 
-	nubus_init_via();
-	printk("Scanning nubus slots.\n");
-	nubus_probe_bus();
 #ifdef CONFIG_PROC_FS
-	proc_register(&proc_root, &proc_nubus);
+	proc_register(&proc_root, &proc_old_nubus);
+	nubus_proc_init();
 #endif
 }
-
-	
\ No newline at end of file
diff -urN linux-2.2.18-pre15/drivers/nubus/nubus_syms.c linux/drivers/nubus/nubus_syms.c
--- linux-2.2.18-pre15/drivers/nubus/nubus_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/nubus/nubus_syms.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,27 @@
+/* Exported symbols for NuBus services
+
+   (c) 1999 David Huggins-Daines <dhd@debian.org> */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/nubus.h>
+
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(nubus_proc_attach_device);
+EXPORT_SYMBOL(nubus_proc_detach_device);
+#endif
+
+EXPORT_SYMBOL(nubus_find_device);
+EXPORT_SYMBOL(nubus_find_type);
+EXPORT_SYMBOL(nubus_find_slot);
+EXPORT_SYMBOL(nubus_get_root_dir);
+EXPORT_SYMBOL(nubus_get_board_dir);
+EXPORT_SYMBOL(nubus_get_func_dir);
+EXPORT_SYMBOL(nubus_readdir);
+EXPORT_SYMBOL(nubus_find_rsrc);
+EXPORT_SYMBOL(nubus_rewinddir);
+EXPORT_SYMBOL(nubus_get_subdir);
+EXPORT_SYMBOL(nubus_get_rsrc_mem);
+EXPORT_SYMBOL(nubus_get_rsrc_str);
+
diff -urN linux-2.2.18-pre15/drivers/nubus/proc.c linux/drivers/nubus/proc.c
--- linux-2.2.18-pre15/drivers/nubus/proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/nubus/proc.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,183 @@
+/* drivers/nubus/proc.c: Proc FS interface for NuBus.
+
+   By David Huggins-Daines <dhd@debian.org>
+
+   Much code and many ideas from drivers/pci/proc.c:
+   Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+
+   This is initially based on the Zorro and PCI interfaces.  However,
+   it works somewhat differently.  The intent is to provide a
+   structure in /proc analogous to the structure of the NuBus ROM
+   resources.
+
+   Therefore each NuBus device is in fact a directory, which may in
+   turn contain subdirectories.  The "files" correspond to NuBus
+   resource records.  For those types of records which we know how to
+   convert to formats that are meaningful to userspace (mostly just
+   icons) these files will provide "cooked" data.  Otherwise they will
+   simply provide raw access (read-only of course) to the ROM.  */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/nubus.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+int
+get_nubus_dev_info(char *buf, char **start, off_t pos, int count, int wr)
+{
+	struct nubus_dev *dev = nubus_devices;
+	off_t at = 0;
+	int len, cnt;
+
+	cnt = 0;
+	while (dev && count > cnt) {
+		len = sprintf(buf, "%x\t%04x %04x %04x %04x",
+			      dev->board->slot,
+			      dev->category,
+			      dev->type,
+			      dev->dr_sw,
+			      dev->dr_hw);
+		len += sprintf(buf+len,
+			       "\t%08lx",
+			       dev->board->slot_addr);
+		buf[len++] = '\n';
+		at += len;
+		if (at >= pos) {
+			if (!*start) {
+				*start = buf + (pos - (at - len));
+				cnt = at - pos;
+			} else
+				cnt += len;
+			buf += len;
+		}
+		dev = dev->next;
+	}
+	return (count > cnt) ? cnt : count;
+}
+
+static struct proc_dir_entry proc_nubus_devices = {
+	PROC_BUS_NUBUS_DEVICES, 7, "devices",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations,
+	get_nubus_dev_info
+};
+
+static struct proc_dir_entry *proc_bus_nubus_dir;
+
+static void nubus_proc_subdir(struct nubus_dev* dev,
+			      struct proc_dir_entry* parent,
+			      struct nubus_dir* dir)
+{
+	struct nubus_dirent ent;
+
+	/* Some of these are directories, others aren't */
+	while (nubus_readdir(dir, &ent) != -1) {
+		char name[8];
+		struct proc_dir_entry* e;
+		
+		sprintf(name, "%x", ent.type);
+		e = create_proc_entry(name, S_IFREG | S_IRUGO |
+				      S_IWUSR, parent);
+		if (!e) return;
+	}
+}
+
+/* Can't do this recursively since the root directory is structured
+   somewhat differently from the subdirectories */
+static void nubus_proc_populate(struct nubus_dev* dev,
+				struct proc_dir_entry* parent,
+				struct nubus_dir* root)
+{
+	struct nubus_dirent ent;
+
+	/* We know these are all directories (board resource + one or
+	   more functional resources) */
+	while (nubus_readdir(root, &ent) != -1) {
+		char name[8];
+		struct proc_dir_entry* e;
+		struct nubus_dir dir;
+		
+		sprintf(name, "%x", ent.type);
+		e = create_proc_entry(name, S_IFDIR, parent);
+		if (!e) return;
+
+		/* And descend */
+		if (nubus_get_subdir(&ent, &dir) == -1) {
+			/* This shouldn't happen */
+			printk(KERN_ERR "NuBus root directory node %x:%x has no subdir!\n",
+			       dev->board->slot, ent.type);
+			continue;
+		} else {
+			nubus_proc_subdir(dev, e, &dir);
+		}
+	}
+}
+
+int nubus_proc_attach_device(struct nubus_dev *dev)
+{
+	struct proc_dir_entry *e;
+	struct nubus_dir root;
+	char name[8];
+
+	if (dev == NULL) {
+		printk(KERN_ERR
+		       "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
+		return -1;
+	}
+		
+	if (dev->board == NULL) {
+		printk(KERN_ERR
+		       "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
+		printk("dev = %p, dev->board = %p\n", dev, dev->board);
+		return -1;
+	}
+		
+	/* Create a directory */
+	sprintf(name, "%x", dev->board->slot);
+	e = dev->procdir = create_proc_entry(name, S_IFDIR,
+					     proc_bus_nubus_dir);
+	if (!e)
+		return -ENOMEM;
+
+	/* Now recursively populate it with files */
+	nubus_get_root_dir(dev->board, &root);
+	nubus_proc_populate(dev, e, &root);
+
+	return 0;
+}
+
+/* FIXME: this is certainly broken! */
+int nubus_proc_detach_device(struct nubus_dev *dev)
+{
+	struct proc_dir_entry *e;
+
+	if ((e = dev->procdir)) {
+		if (e->count)
+			return -EBUSY;
+		remove_proc_entry(e->name, proc_bus_nubus_dir);
+		dev->procdir = NULL;
+	}
+	return 0;
+}
+
+__initfunc(void proc_bus_nubus_add_devices(void))
+{
+	struct nubus_dev *dev;
+	
+	for(dev = nubus_devices; dev; dev = dev->next)
+		nubus_proc_attach_device(dev);
+}
+
+__initfunc(void nubus_proc_init(void))
+{
+	if (!MACH_IS_MAC)
+		return;
+	proc_bus_nubus_dir = create_proc_entry("nubus", S_IFDIR, proc_bus);
+	proc_register(proc_bus_nubus_dir, &proc_nubus_devices);
+	proc_bus_nubus_add_devices();
+}
diff -urN linux-2.2.18-pre15/drivers/scsi/a2091.c linux/drivers/scsi/a2091.c
--- linux-2.2.18-pre15/drivers/scsi/a2091.c	Sat Apr 11 11:13:25 1998
+++ linux/drivers/scsi/a2091.c	Mon Dec  3 20:34:11 2001
@@ -44,30 +44,16 @@
 	    continue;
 
 	if (status & ISTR_INTS)
-	{
-	    /* disable PORTS interrupt */
-	    custom.intena = IF_PORTS;
 	    wd33c93_intr (instance);
-	    /* enable PORTS interrupt */
-	    custom.intena = IF_SETCLR | IF_PORTS;
-	}
     }
 }
 
-static void do_a2091_intr (int irq, void *dummy, struct pt_regs *fp)
-{
-    unsigned long flags;
-
-    spin_lock_irqsave(&io_request_lock, flags);
-    a2091_intr(irq, dummy, fp);
-    spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
 static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
 {
     unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
-    unsigned long addr = VTOP(cmd->SCp.ptr);
+    unsigned long addr = virt_to_bus(cmd->SCp.ptr);
     struct Scsi_Host *instance = cmd->host;
+    static int scsi_alloc_out_of_range = 0;
 
     /* don't allow DMA if the physical address is bad */
     if (addr & A2091_XFER_MASK ||
@@ -75,41 +61,55 @@
     {
 	HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511)
 	    & ~0x1ff;
-	HDATA(instance)->dma_bounce_buffer =
-	    scsi_malloc (HDATA(instance)->dma_bounce_len);
-	
-	/* can't allocate memory; use PIO */
-	if (!HDATA(instance)->dma_bounce_buffer) {
-	    HDATA(instance)->dma_bounce_len = 0;
-	    return 1;
+ 	if( !scsi_alloc_out_of_range ) {
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		scsi_malloc (HDATA(cmd->host)->dma_bounce_len);
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED;
 	}
 
-	/* get the physical address of the bounce buffer */
-	addr = VTOP(HDATA(instance)->dma_bounce_buffer);
+	if ( scsi_alloc_out_of_range || !HDATA(cmd->host)->dma_bounce_buffer) {
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
+
+	    if(!HDATA(cmd->host)->dma_bounce_buffer)
+	    {
+		HDATA(cmd->host)->dma_bounce_len = 0;
+		return 1;
+	    }
 
-	/* the bounce buffer may not be in the first 16M of physmem */
-	if (addr & A2091_XFER_MASK) {
-	    /* we could use chipmem... maybe later */
-	    scsi_free (HDATA(instance)->dma_bounce_buffer,
-		       HDATA(instance)->dma_bounce_len);
-	    HDATA(instance)->dma_bounce_buffer = NULL;
-	    HDATA(instance)->dma_bounce_len = 0;
-	    return 1;
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
 	}
 
+	/* check if the address of the bounce buffer is OK */
+	addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer);
+
+	if (addr & A2091_XFER_MASK) {
+	    /* fall back to Chip RAM if address out of range */
+	    if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) {
+		scsi_free (HDATA(cmd->host)->dma_bounce_buffer,
+			   HDATA(cmd->host)->dma_bounce_len);
+		scsi_alloc_out_of_range = 1;
+	    } else {
+		amiga_chip_free (HDATA(cmd->host)->dma_bounce_buffer);
+            }
+		
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
+
+	    if(!HDATA(cmd->host)->dma_bounce_buffer)
+	    {
+		HDATA(cmd->host)->dma_bounce_len = 0;
+		return 1;
+	    }
+
+	    addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer);
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
+	}
+	    
 	if (!dir_in) {
 	    /* copy to bounce buffer for a write */
-	    if (cmd->use_sg)
-#if 0
-		panic ("scsi%ddma: incomplete s/g support",
-		       instance->host_no);
-#else
-		memcpy (HDATA(instance)->dma_bounce_buffer,
-			cmd->SCp.ptr, cmd->SCp.this_residual);
-#endif
-	    else
-		memcpy (HDATA(instance)->dma_bounce_buffer,
-			cmd->request_buffer, cmd->request_bufflen);
+	    memcpy (HDATA(cmd->host)->dma_bounce_buffer,
+		    cmd->SCp.ptr, cmd->SCp.this_residual);
 	}
     }
 
@@ -169,32 +169,19 @@
 
     /* copy from a bounce buffer, if necessary */
     if (status && HDATA(instance)->dma_bounce_buffer) {
-	if (SCpnt && SCpnt->use_sg) {
-#if 0
-	    panic ("scsi%d: incomplete s/g support",
-		   instance->host_no);
-#else
-	    if( HDATA(instance)->dma_dir )
-		memcpy (SCpnt->SCp.ptr, 
-			HDATA(instance)->dma_bounce_buffer,
-			SCpnt->SCp.this_residual);
-	    scsi_free (HDATA(instance)->dma_bounce_buffer,
-		       HDATA(instance)->dma_bounce_len);
-	    HDATA(instance)->dma_bounce_buffer = NULL;
-	    HDATA(instance)->dma_bounce_len = 0;
-	    
-#endif
-	} else {
-	    if (HDATA(instance)->dma_dir && SCpnt)
-		memcpy (SCpnt->request_buffer,
-			HDATA(instance)->dma_bounce_buffer,
-			SCpnt->request_bufflen);
-
+	if (HDATA(instance)->dma_dir && SCpnt)
+	    memcpy (SCpnt->SCp.ptr, 
+		    HDATA(instance)->dma_bounce_buffer,
+		    SCpnt->SCp.this_residual);
+	
+	if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
 	    scsi_free (HDATA(instance)->dma_bounce_buffer,
 		       HDATA(instance)->dma_bounce_len);
-	    HDATA(instance)->dma_bounce_buffer = NULL;
-	    HDATA(instance)->dma_bounce_len = 0;
-	}
+	else
+	    amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
+	
+	HDATA(instance)->dma_bounce_buffer = NULL;
+	HDATA(instance)->dma_bounce_len = 0;
     }
 }
 
@@ -229,7 +216,7 @@
 	if (num_a2091++ == 0) {
 	    first_instance = instance;
 	    a2091_template = instance->hostt;
-	    request_irq(IRQ_AMIGA_PORTS, do_a2091_intr, 0, "A2091 SCSI", a2091_intr);
+	    request_irq(IRQ_AMIGA_PORTS, a2091_intr, 0, "A2091 SCSI", a2091_intr);
 	}
 	DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
 	zorro_config_board(key, 0);
diff -urN linux-2.2.18-pre15/drivers/scsi/a3000.c linux/drivers/scsi/a3000.c
--- linux-2.2.18-pre15/drivers/scsi/a3000.c	Sat Apr 11 11:13:25 1998
+++ linux/drivers/scsi/a3000.c	Mon Dec  3 20:34:11 2001
@@ -38,28 +38,16 @@
 	return;
     if (status & ISTR_INTS)
     {
-	/* disable PORTS interrupt */
-	custom.intena = IF_PORTS;
 	wd33c93_intr (a3000_host);
-	/* enable PORTS interrupt */
-	custom.intena = IF_SETCLR | IF_PORTS;
     } else {
-      printk("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
+        printk("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
     }
 }
 
-static void do_a3000_intr (int irq, void *dummy, struct pt_regs *fp)
-{
-    unsigned long flags;
-
-    spin_lock_irqsave(&io_request_lock, flags);
-    a3000_intr(irq, dummy, fp);
-    spin_unlock_irqrestore(&io_request_lock, flags);
-}
 static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
 {
     unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
-    unsigned long addr = VTOP(cmd->SCp.ptr);
+    unsigned long addr = virt_to_bus(cmd->SCp.ptr);
 
     /*
      * if the physical address has the wrong alignment, or if
@@ -91,7 +79,7 @@
 			cmd->request_buffer, cmd->request_bufflen);
 	}
 
-	addr = VTOP(HDATA(a3000_host)->dma_bounce_buffer);
+	addr = virt_to_bus(HDATA(a3000_host)->dma_bounce_buffer);
     }
 
     /* setup dma direction */
@@ -194,7 +182,7 @@
     DMA(a3000_host)->DAWR = DAWR_A3000;
     wd33c93_init(a3000_host, (wd33c93_regs *)&(DMA(a3000_host)->SASR),
 		 dma_setup, dma_stop, WD33C93_FS_12_15);
-    request_irq(IRQ_AMIGA_PORTS, do_a3000_intr, 0, "A3000 SCSI", a3000_intr);
+    request_irq(IRQ_AMIGA_PORTS, a3000_intr, 0, "A3000 SCSI", a3000_intr);
     DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN;
     called = 1;
 
diff -urN linux-2.2.18-pre15/drivers/scsi/amiga7xx.c linux/drivers/scsi/amiga7xx.c
--- linux-2.2.18-pre15/drivers/scsi/amiga7xx.c	Mon Oct  5 13:43:31 1998
+++ linux/drivers/scsi/amiga7xx.c	Mon Dec  3 20:34:11 2001
@@ -15,11 +15,14 @@
 #include <linux/config.h>
 #include <linux/zorro.h>
 
+#include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
+#include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/init.h>
 
 #include "scsi.h"
 #include "hosts.h"
@@ -33,11 +36,11 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, 
-		       u32 base, int io_port, int irq, int dma,
-		       long long options, int clock);
+extern int ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, 
+			   u32 base, int io_port, int irq, int dma,
+			   long long options, int clock);
 
-int amiga7xx_detect(Scsi_Host_Template *tpnt)
+__initfunc(int amiga7xx_detect(Scsi_Host_Template *tpnt))
 {
     static unsigned char called = 0;
     unsigned int key;
@@ -54,18 +57,13 @@
     if ((key = zorro_find(ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS, 0, 0)))
     {
 	cd = zorro_get_board(key);
-/*
-	unsigned long address;
-	address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
-		cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL);
-*/
+
 	options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
 
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
-	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(0x80f40000),
-			0, IRQ_AMIGA_PORTS, DMA_NONE, 
-			options, clock);
+	ncr53c7xx_init(tpnt, 0, 710, (u32)ZTWO_VADDR(0xf40000),	0,
+		       IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
 
 	zorro_config_board(key, 0);
 	num++;
@@ -77,16 +75,15 @@
     {
 	unsigned long address;
 	cd = zorro_get_board(key);
-	address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
-		cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL);
+	address = (unsigned long)ioremap((unsigned long)cd->cd_BoardAddr,
+						cd->cd_BoardSize);
 
 	options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
 
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
 	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000),
-			0, IRQ_AMIGA_PORTS, DMA_NONE, 
-			options, clock);
+		       0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
 
 	zorro_config_board(key, 0);
 	num++;
@@ -100,9 +97,8 @@
 
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
-    	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040),
-			0, IRQ_AMIGA_PORTS, DMA_NONE,
-			options, clock);
+    	ncr53c7xx_init(tpnt, 0, 710, (u32)ZTWO_VADDR(0xDD0040), 0,
+		       IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
     	num++;
     }
 #endif
@@ -113,8 +109,8 @@
     {
 	unsigned long address;
 	cd = zorro_get_board(key);
-	address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
-		cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL);
+	address = (unsigned long)ioremap((unsigned long)cd->cd_BoardAddr,
+						cd->cd_BoardSize);
 
     	options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
 
diff -urN linux-2.2.18-pre15/drivers/scsi/atari_NCR5380.c linux/drivers/scsi/atari_NCR5380.c
--- linux-2.2.18-pre15/drivers/scsi/atari_NCR5380.c	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/atari_NCR5380.c	Mon Dec  3 20:34:11 2001
@@ -472,9 +472,9 @@
     int		  cnt = 1;
 #endif
 
-    for (endaddr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
+    for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
 	 cmd->SCp.buffers_residual &&
-	 VTOP(cmd->SCp.buffer[1].address) == endaddr; ) {
+	 virt_to_phys(cmd->SCp.buffer[1].address) == endaddr; ) {
 	
 	MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
 		   cmd->SCp.buffer[1].address, endaddr);
@@ -1459,9 +1459,9 @@
       unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
 
       while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
-	   && jiffies < timeout && !hostdata->connected)
+	   && time_before(jiffies, timeout) && !hostdata->connected)
 	;
-      if (jiffies >= timeout)
+      if (time_after_eq(jiffies, timeout))
       {
 	printk("scsi : arbitration timeout at %d\n", __LINE__);
 	NCR5380_write(MODE_REG, MR_BASE);
@@ -1616,7 +1616,7 @@
      * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
      */
 
-    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & 
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & 
 	(SR_BSY | SR_IO)));
 
     if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
@@ -1629,7 +1629,7 @@
 	    return -1;
     }
 #else
-    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
 #endif
 
     /* 
diff -urN linux-2.2.18-pre15/drivers/scsi/atari_scsi.c linux/drivers/scsi/atari_scsi.c
--- linux-2.2.18-pre15/drivers/scsi/atari_scsi.c	Thu Jul 30 11:17:10 1998
+++ linux/drivers/scsi/atari_scsi.c	Mon Dec  3 20:34:11 2001
@@ -132,51 +132,51 @@
 	} while(0)
 
 #define	SCSI_DMA_READ_P(elt)					\
-	(((unsigned long)tt_scsi_dma.elt##_hi  << 24) |		\
-	 ((unsigned long)tt_scsi_dma.elt##_hmd << 16) |		\
-	 ((unsigned long)tt_scsi_dma.elt##_lmd << 8) |		\
-	  (unsigned long)tt_scsi_dma.elt##_lo)
-
-
-#define	SCSI_DMA_SETADR(adr)				\
-    do {						\
-	unsigned long __adr = (adr);			\
-	st_dma.dma_lo = (unsigned char)__adr;		\
-	MFPDELAY();					\
-	__adr >>= 8;					\
-	st_dma.dma_md = (unsigned char)__adr;		\
-	MFPDELAY();					\
-	__adr >>= 8;					\
-	st_dma.dma_hi = (unsigned char)__adr;		\
-	MFPDELAY();					\
-    } while(0)
-
-#define	SCSI_DMA_GETADR() ({				\
-    unsigned long	__adr;				\
-    __adr = st_dma.dma_lo;				\
-    MFPDELAY();						\
-    __adr |= (st_dma.dma_md & 0xff) << 8;		\
-    MFPDELAY();						\
-    __adr |= (st_dma.dma_hi & 0xff) << 16;		\
-    MFPDELAY();						\
-    __adr;						\
-})
-
-#define	ENABLE_IRQ()					\
-	do {						\
-		if (IS_A_TT())				\
-			atari_enable_irq( IRQ_TT_MFP_SCSI );	\
-		else					\
-			atari_enable_irq( IRQ_MFP_FSCSI );	\
-	} while(0)
-
-#define	DISABLE_IRQ()					\
-	do {						\
-		if (IS_A_TT())				\
-			atari_disable_irq( IRQ_TT_MFP_SCSI );	\
-		else					\
-			atari_disable_irq( IRQ_MFP_FSCSI );	\
-	} while(0)
+	(((((((unsigned long)tt_scsi_dma.elt##_hi << 8) |	\
+	     (unsigned long)tt_scsi_dma.elt##_hmd) << 8) |	\
+	   (unsigned long)tt_scsi_dma.elt##_lmd) << 8) |	\
+	 (unsigned long)tt_scsi_dma.elt##_lo)
+
+
+static inline void SCSI_DMA_SETADR(unsigned long adr)
+{
+	st_dma.dma_lo = (unsigned char)adr;
+	MFPDELAY();
+	adr >>= 8;
+	st_dma.dma_md = (unsigned char)adr;
+	MFPDELAY();
+	adr >>= 8;
+	st_dma.dma_hi = (unsigned char)adr;
+	MFPDELAY();
+}
+
+static inline unsigned long SCSI_DMA_GETADR(void)
+{
+	unsigned long adr;
+	adr = st_dma.dma_lo;
+	MFPDELAY();
+	adr |= (st_dma.dma_md & 0xff) << 8;
+	MFPDELAY();
+	adr |= (st_dma.dma_hi & 0xff) << 16;
+	MFPDELAY();
+	return adr;
+}
+
+static inline void ENABLE_IRQ(void)
+{
+	if (IS_A_TT())
+		atari_enable_irq(IRQ_TT_MFP_SCSI);
+	else
+		atari_enable_irq(IRQ_MFP_FSCSI);
+}
+
+static inline void DISABLE_IRQ(void)
+{
+	if (IS_A_TT())
+		atari_disable_irq(IRQ_TT_MFP_SCSI);
+	else
+		atari_disable_irq(IRQ_MFP_FSCSI);
+}
 
 
 #define HOSTDATA_DMALEN		(((struct NCR5380_hostdata *) \
@@ -461,8 +461,8 @@
 		/* If the dribble buffer was used on a read operation, copy the DMA-ed
 		 * data to the original destination address.
 		 */
-		memcpy( atari_dma_orig_addr, (void *)PTOV(atari_dma_startaddr),
-		       HOSTDATA_DMALEN - atari_dma_residual );
+		memcpy(atari_dma_orig_addr, phys_to_virt(atari_dma_startaddr),
+		       HOSTDATA_DMALEN - atari_dma_residual);
 		atari_dma_orig_addr = NULL;
 	}
 
@@ -476,20 +476,22 @@
 static void atari_scsi_fetch_restbytes( void )
 {
 	int nr;
-	char	*src, *dst;
+	char *src, *dst;
+	unsigned long phys_dst;
 
 	/* fetch rest bytes in the DMA register */
-	dst = (char *)SCSI_DMA_READ_P( dma_addr );
-	if ((nr = ((long)dst & 3))) {
-		/* there are 'nr' bytes left for the last long address before the
-		   DMA pointer */
-		dst = (char *)( (unsigned long)dst & ~3 );
+	phys_dst = SCSI_DMA_READ_P(dma_addr);
+	nr = phys_dst & 3;
+	if (nr) {
+		/* there are 'nr' bytes left for the last long address
+		   before the DMA pointer */
+		phys_dst ^= nr;
 		DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx",
-			   nr, (long)dst);
-		dst = (char *)PTOV(dst);  /* The content of the DMA pointer
-					   * is a physical address! */
-		DMA_PRINTK(" = virt addr 0x%08lx\n", (long)dst);
-		for( src = (char *)&tt_scsi_dma.dma_restdata; nr > 0; --nr )
+			   nr, phys_dst);
+		/* The content of the DMA pointer is a physical address!  */
+		dst = phys_to_virt(phys_dst);
+		DMA_PRINTK(" = virt addr %p\n", dst);
+		for (src = (char *)&tt_scsi_dma.dma_restdata; nr != 0; --nr)
 			*dst++ = *src++;
 	}
 }
@@ -665,7 +667,7 @@
 					"double buffer\n" );
 			return( 0 );
 		}
-		atari_dma_phys_buffer = VTOP( atari_dma_buffer );
+		atari_dma_phys_buffer = virt_to_phys( atari_dma_buffer );
 		atari_dma_orig_addr = 0;
 	}
 #endif
@@ -764,7 +766,7 @@
 }
 #endif
 
-__initfunc(void atari_scsi_setup( char *str, int *ints ))
+void __init atari_scsi_setup(char *str, int *ints)
 {
 	/* Format of atascsi parameter is:
 	 *   atascsi=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
@@ -772,23 +774,6 @@
 	 * Negative values mean don't change.
 	 */
 	
-	/* Grmbl... the standard parameter parsing can't handle negative numbers
-	 * :-( So let's do it ourselves!
-	 */
-
-	int i = ints[0]+1, fact;
-
-	while( str && (isdigit(*str) || *str == '-') && i <= 10) {
-		if (*str == '-')
-			fact = -1, ++str;
-		else
-			fact = 1;
-		ints[i++] = simple_strtoul( str, NULL, 0 ) * fact;
-		if ((str = strchr( str, ',' )) != NULL)
-			++str;
-	}
-	ints[0] = i-1;
-	
 	if (ints[0] < 1) {
 		printk( "atari_scsi_setup: no arguments!\n" );
 		return;
@@ -869,7 +854,7 @@
 
 	
 #ifdef CONFIG_ATARI_SCSI_RESET_BOOT
-__initfunc(static void atari_scsi_reset_boot( void ))
+static void __init atari_scsi_reset_boot(void)
 {
 	unsigned long end;
 	
@@ -892,7 +877,8 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	end = jiffies + AFTER_RESET_DELAY;
+	while (time_before(jiffies, end))
 		barrier();
 
 	printk( " done\n" );
@@ -913,7 +899,7 @@
 unsigned long atari_scsi_dma_setup( struct Scsi_Host *instance, void *data,
 				   unsigned long count, int dir )
 {
-	unsigned long addr = VTOP( data );
+	unsigned long addr = virt_to_phys( data );
 
 	DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, "
 		   "dir = %d\n", instance->host_no, data, addr, count, dir);
@@ -1097,7 +1083,7 @@
 	}
 	
 	/* Last step: apply the hard limit on DMA transfers */
-	limit = (atari_dma_buffer && !STRAM_ADDR( VTOP(cmd->SCp.ptr) )) ?
+	limit = (atari_dma_buffer && !STRAM_ADDR( virt_to_phys(cmd->SCp.ptr) )) ?
 		    STRAM_BUFFER_SIZE : 255*512;
 	if (possible_len > limit)
 		possible_len = limit;
diff -urN linux-2.2.18-pre15/drivers/scsi/gvp11.c linux/drivers/scsi/gvp11.c
--- linux-2.2.18-pre15/drivers/scsi/gvp11.c	Sat Apr 11 11:13:25 1998
+++ linux/drivers/scsi/gvp11.c	Mon Dec  3 20:34:11 2001
@@ -44,23 +44,10 @@
 	if (!(status & GVP11_DMAC_INT_PENDING))
 	    continue;
 
-	/* disable PORTS interrupt */
-	custom.intena = IF_PORTS;
 	wd33c93_intr (instance);
-	/* enable PORTS interrupt */
-	custom.intena = IF_SETCLR | IF_PORTS;
     }
 }
 
-static void do_gvp11_intr (int irq, void *dummy, struct pt_regs *fp)
-{
-    unsigned long flags;
-
-    spin_lock_irqsave(&io_request_lock, flags);
-    gvp11_intr(irq, dummy, fp);
-    spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
 static int gvp11_xfer_mask = 0;
 
 void gvp11_setup (char *str, int *ints)
@@ -71,20 +58,24 @@
 static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
 {
     unsigned short cntr = GVP11_DMAC_INT_ENABLE;
-    unsigned long addr = VTOP(cmd->SCp.ptr);
+    unsigned long addr = virt_to_bus(cmd->SCp.ptr);
     int bank_mask;
+    static int scsi_alloc_out_of_range = 0;
 
-    /* don't allow DMA if the physical address is bad */
+    /* use bounce buffer if the physical address is bad */
     if (addr & HDATA(cmd->host)->dma_xfer_mask ||
 	(!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual)))
     {
 	HDATA(cmd->host)->dma_bounce_len = (cmd->SCp.this_residual + 511)
 	    & ~0x1ff;
-	HDATA(cmd->host)->dma_bounce_buffer =
-	    scsi_malloc (HDATA(cmd->host)->dma_bounce_len);
-	HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED;
-	
-	if (!HDATA(cmd->host)->dma_bounce_buffer) {
+
+ 	if( !scsi_alloc_out_of_range ) {
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		scsi_malloc (HDATA(cmd->host)->dma_bounce_len);
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED;
+	}
+
+	if ( scsi_alloc_out_of_range || !HDATA(cmd->host)->dma_bounce_buffer) {
 	    HDATA(cmd->host)->dma_bounce_buffer =
 		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
 
@@ -98,15 +89,17 @@
 	}
 
 	/* check if the address of the bounce buffer is OK */
-	addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer);
+	addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer);
 
 	if (addr & HDATA(cmd->host)->dma_xfer_mask) {
 	    /* fall back to Chip RAM if address out of range */
-	    if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+	    if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) {
 		scsi_free (HDATA(cmd->host)->dma_bounce_buffer,
 			   HDATA(cmd->host)->dma_bounce_len);
-	    else
+		scsi_alloc_out_of_range = 1;
+	    } else {
 		amiga_chip_free (HDATA(cmd->host)->dma_bounce_buffer);
+            }
 		
 	    HDATA(cmd->host)->dma_bounce_buffer =
 		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
@@ -117,18 +110,14 @@
 		return 1;
 	    }
 
-	    addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer);
+	    addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer);
 	    HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
 	}
 	    
 	if (!dir_in) {
 	    /* copy to bounce buffer for a write */
-	    if (cmd->use_sg)
-		memcpy (HDATA(cmd->host)->dma_bounce_buffer,
-				cmd->SCp.ptr, cmd->SCp.this_residual);
-	    else
-		memcpy (HDATA(cmd->host)->dma_bounce_buffer,
-			cmd->request_buffer, cmd->request_bufflen);
+	    memcpy (HDATA(cmd->host)->dma_bounce_buffer,
+		    cmd->SCp.ptr, cmd->SCp.this_residual);
 	}
     }
 
@@ -169,41 +158,19 @@
 
     /* copy from a bounce buffer, if necessary */
     if (status && HDATA(instance)->dma_bounce_buffer) {
-	if (SCpnt && SCpnt->use_sg) {
-#if 0
-	    panic ("scsi%d: incomplete s/g support",
-		   instance->host_no);
-#else
-	    if( HDATA(instance)->dma_dir )
-		memcpy (SCpnt->SCp.ptr, 
-			HDATA(instance)->dma_bounce_buffer,
-			SCpnt->SCp.this_residual);
-
-	    if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
-		scsi_free (HDATA(instance)->dma_bounce_buffer,
-			    HDATA(instance)->dma_bounce_len);
-	    else
-		amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
-
-	    HDATA(instance)->dma_bounce_buffer = NULL;
-	    HDATA(instance)->dma_bounce_len = 0;
-	    
-#endif
-	} else {
-	    if (HDATA(instance)->dma_dir && SCpnt)
-		memcpy (SCpnt->request_buffer,
-			HDATA(instance)->dma_bounce_buffer,
-			SCpnt->request_bufflen);
-
-	    if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
-		scsi_free (HDATA(instance)->dma_bounce_buffer,
-			    HDATA(instance)->dma_bounce_len);
-	    else
-		amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
-
-	    HDATA(instance)->dma_bounce_buffer = NULL;
-	    HDATA(instance)->dma_bounce_len = 0;
-	}
+	if (HDATA(instance)->dma_dir && SCpnt)
+	    memcpy (SCpnt->SCp.ptr, 
+		    HDATA(instance)->dma_bounce_buffer,
+		    SCpnt->SCp.this_residual);
+	
+	if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+	    scsi_free (HDATA(instance)->dma_bounce_buffer,
+		       HDATA(instance)->dma_bounce_len);
+	else
+	    amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
+	
+	HDATA(instance)->dma_bounce_buffer = NULL;
+	HDATA(instance)->dma_bounce_len = 0;
     }
 }
 
@@ -371,7 +338,7 @@
 	if (num_gvp11++ == 0) {
 		first_instance = instance;
 		gvp11_template = instance->hostt;
-		request_irq(IRQ_AMIGA_PORTS, do_gvp11_intr, 0,
+		request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0,
 			    "GVP11 SCSI", gvp11_intr);
 	}
 	DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
diff -urN linux-2.2.18-pre15/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- linux-2.2.18-pre15/drivers/scsi/ide-scsi.c	Wed Jun  7 14:26:43 2000
+++ linux/drivers/scsi/ide-scsi.c	Mon Dec  3 20:34:11 2001
@@ -96,6 +96,7 @@
 	unsigned long flags;			/* Status/Action flags */
 	unsigned long transform;		/* SCSI cmd translation layer */
 	unsigned long log;			/* log flags */
+	int last_lun;				/* last LUN */
 } idescsi_scsi_t;
 
 /*
@@ -508,8 +509,11 @@
 	drive->ready_stat = 0;
 	memset (scsi, 0, sizeof (idescsi_scsi_t));
 	scsi->drive = drive;
-	if (drive->id && (drive->id->config & 0x0060) == 0x20)
-		set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+	if (drive->id) {
+		if ((drive->id->config & 0x0060) == 0x20)
+			set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+		scsi->last_lun = drive->id->last_lun;
+	}
 	set_bit(IDESCSI_TRANSFORM, &scsi->transform);
 	clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
 #if IDESCSI_DEBUG_LOG
@@ -603,11 +607,15 @@
 int idescsi_detect (Scsi_Host_Template *host_template)
 {
 	struct Scsi_Host *host;
+	int last_lun = 0;
 	int id;
 
 	host_template->proc_dir = &idescsi_proc_dir;
 	host = scsi_register(host_template, 0);
-	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++)
+		last_lun = IDE_MAX(last_lun, idescsi_drives[id]->id->last_lun);
+
+	host->max_lun = last_lun + 1;
 	host->max_id = id;
 	host->can_queue = host->cmd_per_lun * id;
 	return 1;
@@ -736,20 +744,24 @@
 
 	if (!drive) {
 		printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
-		goto abort;
+		goto error;
 	}
 	scsi = drive->driver_data;
 	pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
 	rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
 	if (rq == NULL || pc == NULL) {
 		printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
-		goto abort;
+		goto error;
 	}
 
 	memset (pc->c, 0, 12);
 	pc->flags = 0;
 	pc->rq = rq;
 	memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+	if ((pc->c[1] >> 5) > scsi->last_lun) {
+		cmd->result = DID_ABORT << 16;
+		goto abort;
+	}
 	if (cmd->use_sg) {
 		pc->buffer = NULL;
 		pc->sg = cmd->request_buffer;
@@ -784,10 +796,12 @@
 	(void) ide_do_drive_cmd (drive, rq, ide_end);
 	spin_lock_irq(&io_request_lock);
 	return 0;
+
+error:
+	cmd->result = DID_ERROR << 16;
 abort:
 	if (pc) kfree (pc);
 	if (rq) kfree (rq);
-	cmd->result = DID_ERROR << 16;
 	done(cmd);
 	return 0;
 }
diff -urN linux-2.2.18-pre15/drivers/scsi/mac_NCR5380.c linux/drivers/scsi/mac_NCR5380.c
--- linux-2.2.18-pre15/drivers/scsi/mac_NCR5380.c	Fri Jan 15 14:41:04 1999
+++ linux/drivers/scsi/mac_NCR5380.c	Wed Dec 31 16:00:00 1969
@@ -1,3139 +0,0 @@
-/* 
- * NCR 5380 generic driver routines.  These should make it *trivial*
- * 	to implement 5380 SCSI drivers under Linux with a non-trantor
- *	architecture.
- *
- *	Note that these routines also work with NR53c400 family chips.
- *
- * Copyright 1993, Drew Eckhardt
- *	Visionary Computing 
- *	(Unix and Linux consulting and custom programming)
- * 	drew@colorado.edu
- *	+1 (303) 666-5836
- *
- * DISTRIBUTION RELEASE 6. 
- *
- * For more information, please consult 
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
- * this file, too:
- *
- *  - Some of the debug statements were incorrect (undefined variables and the
- *    like). I fixed that.
- *
- *  - In information_transfer(), I think a #ifdef was wrong. Looking at the
- *    possible DMA transfer size should also happen for REAL_DMA. I added this
- *    in the #if statement.
- *
- *  - When using real DMA, information_transfer() should return in a DATAOUT
- *    phase after starting the DMA. It has nothing more to do.
- *
- *  - The interrupt service routine should run main after end of DMA, too (not
- *    only after RESELECTION interrupts). Additionally, it should _not_ test
- *    for more interrupts after running main, since a DMA process may have
- *    been started and interrupts are turned on now. The new int could happen
- *    inside the execution of NCR5380_intr(), leading to recursive
- *    calls.
- *
- *  - I've added a function merge_contiguous_buffers() that tries to
- *    merge scatter-gather buffers that are located at contiguous
- *    physical addresses and can be processed with the same DMA setup.
- *    Since most scatter-gather operations work on a page (4K) of
- *    4 buffers (1K), in more than 90% of all cases three interrupts and
- *    DMA setup actions are saved.
- *
- * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
- *    and USLEEP, because these were messing up readability and will never be
- *    needed for Atari SCSI.
- * 
- * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
- *   stuff), and 'main' is executed in a bottom half if awoken by an
- *   interrupt.
- *
- * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
- *   constructs. In my eyes, this made the source rather unreadable, so I
- *   finally replaced that by the *_PRINTK() macros.
- *
- */
-
-/*
- * Further development / testing that should be done : 
- * 1.  Test linked command handling code after Eric is ready with 
- *     the high level code.
- */
-
-/* 
- * Michael: To port Romans driver to the Macintosh, I've left most of the code 
- * unchanged, in order to make later implemantation of REAL_DMA easier. 
- *
- * Alan: In order to make it easier to read and as the 5380 based Mac's never
- *	  have DMA I took the real DMA out of mac_scsi.c but not this file.
- *
- *	With luck we can merge this back with the ST folks in time.
- *
- * Changes:
- * 
- * - all Falcon-specific stuff (ST-DMA locking) was removed
- *
- * 
- */
-
-#if (NDEBUG & NDEBUG_LISTS)
-#define LIST(x,y) \
-  { printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
-    if ((x)==(y)) udelay(5); }
-#define REMOVE(w,x,y,z) \
-  { printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, \
-	   (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
-    if ((x)==(y)) udelay(5); }
-#else
-#define LIST(x,y)
-#define REMOVE(w,x,y,z)
-#endif
-
-#ifndef notyet
-#undef LINKED
-#endif
-
-/*
- * Design
- * Issues :
- *
- * The other Linux SCSI drivers were written when Linux was Intel PC-only,
- * and specifically for each board rather than each chip.  This makes their
- * adaptation to platforms like the Mac (Some of which use NCR5380's)
- * more difficult than it has to be.
- *
- * Also, many of the SCSI drivers were written before the command queuing
- * routines were implemented, meaning their implementations of queued 
- * commands were hacked on rather than designed in from the start.
- *
- * When I designed the Linux SCSI drivers I figured that 
- * while having two different SCSI boards in a system might be useful
- * for debugging things, two of the same type wouldn't be used.
- * Well, I was wrong and a number of users have mailed me about running
- * multiple high-performance SCSI boards in a server.
- *
- * Finally, when I get questions from users, I have no idea what 
- * revision of my driver they are running.
- *
- * This driver attempts to address these problems :
- * This is a generic 5380 driver.  To use it on a different platform, 
- * one simply writes appropriate system specific macros (ie, data
- * transfer - some PC's will use the I/O bus, 68K's must use 
- * memory mapped) and drops this file in their 'C' wrapper.
- *
- * As far as command queueing, two queues are maintained for 
- * each 5380 in the system - commands that haven't been issued yet,
- * and commands that are currently executing.  This means that an 
- * unlimited number of commands may be queued, letting 
- * more commands propagate from the higher driver levels giving higher 
- * throughput.  Note that both I_T_L and I_T_L_Q nexuses are supported, 
- * allowing multiple commands to propagate all the way to a SCSI-II device 
- * while a command is already executing.
- *
- * To solve the multiple-boards-in-the-same-system problem, 
- * there is a separate instance structure for each instance
- * of a 5380 in the system.  So, multiple NCR5380 drivers will
- * be able to coexist with appropriate changes to the high level
- * SCSI code.  
- *
- * A NCR5380_PUBLIC_REVISION macro is provided, with the release
- * number (updated for each public release) printed by the 
- * NCR5380_print_options command, which should be called from the 
- * wrapper detect function, so that I know what release of the driver
- * users are using.
- *
- * Issues specific to the NCR5380 : 
- *
- * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead 
- * piece of hardware that requires you to sit in a loop polling for 
- * the REQ signal as long as you are connected.  Some devices are 
- * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect 
- * while doing long seek operations.
- * 
- * The workaround for this is to keep track of devices that have
- * disconnected.  If the device hasn't disconnected, for commands that
- * should disconnect, we do something like 
- *
- * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- * 
- * Some tweaking of N and M needs to be done.  An algorithm based 
- * on "time to data" would give the best results as long as short time
- * to datas (ie, on the same track) were considered, however these 
- * broken devices are the exception rather than the rule and I'd rather
- * spend my time optimizing for the normal case.
- *
- * Architecture :
- *
- * At the heart of the design is a coroutine, NCR5380_main,
- * which is started when not running by the interrupt handler,
- * timer, and queue command function.  It attempts to establish
- * I_T_L or I_T_L_Q nexuses by removing the commands from the 
- * issue queue and calling NCR5380_select() if a nexus 
- * is not established. 
- *
- * Once a nexus is established, the NCR5380_information_transfer()
- * phase goes through the various phases as instructed by the target.
- * if the target goes into MSG IN and sends a DISCONNECT message,
- * the command structure is placed into the per instance disconnected
- * queue, and NCR5380_main tries to find more work.  If USLEEP
- * was defined, and the target is idle for too long, the system
- * will try to sleep.
- *
- * If a command has disconnected, eventually an interrupt will trigger,
- * calling NCR5380_intr()  which will in turn call NCR5380_reselect
- * to reestablish a nexus.  This will run main if necessary.
- *
- * On command termination, the done function will be called as 
- * appropriate.
- *
- * SCSI pointers are maintained in the SCp field of SCSI command 
- * structures, being initialized after the command is connected
- * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
- * Note that in violation of the standard, an implicit SAVE POINTERS operation
- * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
- */
-
-/*
- * Using this file :
- * This file a skeleton Linux SCSI driver for the NCR 5380 series
- * of chips.  To use it, you write a architecture specific functions 
- * and macros and include this file in your driver.
- *
- * These macros control options : 
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- *	for commands that return with a CHECK CONDITION status. 
- *
- * LINKED - if defined, linked commands are supported.
- *
- * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
- *
- * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
- *
- * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
- *
- * These macros MUST be defined :
- * 
- * NCR5380_read(register)  - read from the specified register
- *
- * NCR5380_write(register, value) - write to the specific register 
- *
- * Either real DMA *or* pseudo DMA may be implemented
- * REAL functions : 
- * NCR5380_REAL_DMA should be defined if real DMA is to be used.
- * Note that the DMA setup functions should return the number of bytes 
- *	that they were able to program the controller for.
- *
- * Also note that generic i386/PC versions of these macros are 
- *	available as NCR5380_i386_dma_write_setup,
- *	NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
- *
- * NCR5380_dma_write_setup(instance, src, count) - initialize
- * NCR5380_dma_read_setup(instance, dst, count) - initialize
- * NCR5380_dma_residual(instance); - residual count
- *
- * PSEUDO functions :
- * NCR5380_pwrite(instance, src, count)
- * NCR5380_pread(instance, dst, count);
- *
- * If nothing specific to this implementation needs doing (ie, with external
- * hardware), you must also define 
- *  
- * NCR5380_queue_command
- * NCR5380_reset
- * NCR5380_abort
- * NCR5380_proc_info
- *
- * to be the global entry points into the specific driver, ie 
- * #define NCR5380_queue_command t128_queue_command.
- *
- * If this is not done, the routines will be defined as static functions
- * with the NCR5380* names and the user must provide a globally
- * accessible wrapper function.
- *
- * The generic driver is initialized by calling NCR5380_init(instance),
- * after setting the appropriate host specific fields and ID.  If the 
- * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- * possible) function may be used.  Before the specific driver initialization
- * code finishes, NCR5380_print_options should be called.
- */
-
-static struct Scsi_Host *first_instance = NULL;
-static Scsi_Host_Template *the_template = NULL;
-
-/* Macros ease life... :-) */
-#define	SETUP_HOSTDATA(in)				\
-    struct NCR5380_hostdata *hostdata =			\
-	(struct NCR5380_hostdata *)(in)->hostdata
-#define	HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
-
-#define	NEXT(cmd)	((Scsi_Cmnd *)((cmd)->host_scribble))
-#define	NEXTADDR(cmd)	((Scsi_Cmnd **)&((cmd)->host_scribble))
-
-#define	HOSTNO		instance->host_no
-#define	H_NO(cmd)	(cmd)->host->host_no
-
-#ifdef SUPPORT_TAGS
-
-/*
- * Functions for handling tagged queuing
- * =====================================
- *
- * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
- *
- * Using consecutive numbers for the tags is no good idea in my eyes. There
- * could be wrong re-usings if the counter (8 bit!) wraps and some early
- * command has been preempted for a long time. My solution: a bitfield for
- * remembering used tags.
- *
- * There's also the problem that each target has a certain queue size, but we
- * cannot know it in advance :-( We just see a QUEUE_FULL status being
- * returned. So, in this case, the driver internal queue size assumption is
- * reduced to the number of active tags if QUEUE_FULL is returned by the
- * target. The command is returned to the mid-level, but with status changed
- * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
- * correctly.
- *
- * We're also not allowed running tagged commands as long as an untagged
- * command is active. And REQUEST SENSE commands after a contingent allegiance
- * condition _must_ be untagged. To keep track whether an untagged command has
- * been issued, the host->busy array is still employed, as it is without
- * support for tagged queuing.
- *
- * One could suspect that there are possible race conditions between
- * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
- * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
- * which already guaranteed to be running at most once. It is also the only
- * place where tags/LUNs are allocated. So no other allocation can slip
- * between that pair, there could only happen a reselection, which can free a
- * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
- * important: the tag bit must be cleared before 'nr_allocated' is decreased.
- */
-
-/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
-#undef TAG_NONE
-#define TAG_NONE 0xff
-
-/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
-#if (MAX_TAGS % 32) != 0
-#error "MAX_TAGS must be a multiple of 32!"
-#endif
-
-typedef struct {
-    char	allocated[MAX_TAGS/8];
-    int		nr_allocated;
-    int		queue_size;
-} TAG_ALLOC;
-
-static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
-
-
-static void init_tags( void )
-{
-    int target, lun;
-    TAG_ALLOC *ta;
-    
-    if (!setup_use_tagged_queuing)
-	return;
-    
-    for( target = 0; target < 8; ++target ) {
-	for( lun = 0; lun < 8; ++lun ) {
-	    ta = &TagAlloc[target][lun];
-	    memset( &ta->allocated, 0, MAX_TAGS/8 );
-	    ta->nr_allocated = 0;
-	    /* At the beginning, assume the maximum queue size we could
-	     * support (MAX_TAGS). This value will be decreased if the target
-	     * returns QUEUE_FULL status.
-	     */
-	    ta->queue_size = MAX_TAGS;
-	}
-    }
-}
-
-
-/* Check if we can issue a command to this LUN: First see if the LUN is marked
- * busy by an untagged command. If the command should use tagged queuing, also
- * check that there is a free tag and the target's queue won't overflow. This
- * function should be called with interrupts disabled to avoid race
- * conditions.
- */ 
-
-static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
-{
-    SETUP_HOSTDATA(cmd->host);
-
-    if (hostdata->busy[cmd->target] & (1 << cmd->lun))
-	return( 1 );
-    if (!should_be_tagged ||
-	!setup_use_tagged_queuing || !cmd->device->tagged_supported)
-	return( 0 );
-    if (TagAlloc[cmd->target][cmd->lun].nr_allocated >=
-	TagAlloc[cmd->target][cmd->lun].queue_size ) {
-	TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
-		    H_NO(cmd), cmd->target, cmd->lun );
-	return( 1 );
-    }
-    return( 0 );
-}
-
-
-/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
- * must be called before!), or reserve the LUN in 'busy' if the command is
- * untagged.
- */
-
-static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
-{
-    SETUP_HOSTDATA(cmd->host);
-
-    /* If we or the target don't support tagged queuing, allocate the LUN for
-     * an untagged command.
-     */
-    if (!should_be_tagged ||
-	!setup_use_tagged_queuing || !cmd->device->tagged_supported) {
-	cmd->tag = TAG_NONE;
-	hostdata->busy[cmd->target] |= (1 << cmd->lun);
-	TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
-		    "command\n", H_NO(cmd), cmd->target, cmd->lun );
-    }
-    else {
-	TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
-
-	cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
-	set_bit( cmd->tag, &ta->allocated );
-	ta->nr_allocated++;
-	TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
-		    "(now %d tags in use)\n",
-		    H_NO(cmd), cmd->tag, cmd->target, cmd->lun,
-		    ta->nr_allocated );
-    }
-}
-
-
-/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
- * unlock the LUN.
- */
-
-static void cmd_free_tag( Scsi_Cmnd *cmd )
-{
-    SETUP_HOSTDATA(cmd->host);
-
-    if (cmd->tag == TAG_NONE) {
-	hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-	TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
-		    H_NO(cmd), cmd->target, cmd->lun );
-    }
-    else if (cmd->tag >= MAX_TAGS) {
-	printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
-		H_NO(cmd), cmd->tag );
-    }
-    else {
-	TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
-	clear_bit( cmd->tag, &ta->allocated );
-	ta->nr_allocated--;
-	TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
-		    H_NO(cmd), cmd->tag, cmd->target, cmd->lun );
-    }
-}
-
-
-static void free_all_tags( void )
-{
-    int target, lun;
-    TAG_ALLOC *ta;
-
-    if (!setup_use_tagged_queuing)
-	return;
-    
-    for( target = 0; target < 8; ++target ) {
-	for( lun = 0; lun < 8; ++lun ) {
-	    ta = &TagAlloc[target][lun];
-	    memset( &ta->allocated, 0, MAX_TAGS/8 );
-	    ta->nr_allocated = 0;
-	}
-    }
-}
-
-#endif /* SUPPORT_TAGS */
-
-
-/*
- * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
- *
- * Purpose: Try to merge several scatter-gather requests into one DMA
- *    transfer. This is possible if the scatter buffers lie on
- *    physical contiguous addresses.
- *
- * Parameters: Scsi_Cmnd *cmd
- *    The command to work on. The first scatter buffer's data are
- *    assumed to be already transfered into ptr/this_residual.
- */
-
-static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
-{
-    unsigned long endaddr;
-#if (NDEBUG & NDEBUG_MERGING)
-    unsigned long oldlen = cmd->SCp.this_residual;
-    int		  cnt = 1;
-#endif
-
-    for (endaddr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
-	 cmd->SCp.buffers_residual &&
-	 VTOP(cmd->SCp.buffer[1].address) == endaddr; ) {
-	
-	MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
-		   cmd->SCp.buffer[1].address, endaddr);
-#if (NDEBUG & NDEBUG_MERGING)
-	++cnt;
-#endif
-	++cmd->SCp.buffer;
-	--cmd->SCp.buffers_residual;
-	cmd->SCp.this_residual += cmd->SCp.buffer->length;
-	endaddr += cmd->SCp.buffer->length;
-    }
-#if (NDEBUG & NDEBUG_MERGING)
-    if (oldlen != cmd->SCp.this_residual)
-	MER_PRINTK("merged %d buffers from %p, new length %08x\n",
-		   cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
-#endif
-}
-
-/*
- * Function : void initialize_SCp(Scsi_Cmnd *cmd)
- *
- * Purpose : initialize the saved data pointers for cmd to point to the 
- *	start of the buffer.
- *
- * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
- */
-
-static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
-{
-    /* 
-     * Initialize the Scsi Pointer field so that all of the commands in the 
-     * various queues are valid.
-     */
-
-    if (cmd->use_sg) {
-	cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
-	cmd->SCp.buffers_residual = cmd->use_sg - 1;
-	cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
-	cmd->SCp.this_residual = cmd->SCp.buffer->length;
-	/* ++roman: Try to merge some scatter-buffers if they are at
-	 * contiguous physical addresses.
-	 */
-	merge_contiguous_buffers( cmd );
-    } else {
-	cmd->SCp.buffer = NULL;
-	cmd->SCp.buffers_residual = 0;
-	cmd->SCp.ptr = (char *) cmd->request_buffer;
-	cmd->SCp.this_residual = cmd->request_bufflen;
-    }
-}
-
-#include <linux/config.h>
-#include <linux/delay.h>
-
-#if 1
-static struct {
-    unsigned char mask;
-    const char * name;} 
-signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, 
-    { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD,  "CD" }, { SR_IO, "IO" }, 
-    { SR_SEL, "SEL" }, {0, NULL}}, 
-basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
-icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
-    {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, 
-    {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, 
-    {0, NULL}},
-mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, 
-    {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, 
-    "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
-    {MR_MONITOR_BSY, "MODE MONITOR BSY"},
-    {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, 
-    {0, NULL}};
-
-/*
- * Function : void NCR5380_print(struct Scsi_Host *instance)
- *
- * Purpose : print the SCSI bus signals for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print(struct Scsi_Host *instance) {
-    unsigned char status, data, basr, mr, icr, i;
-    unsigned long flags;
-
-    save_flags(flags);
-    cli();
-    data = NCR5380_read(CURRENT_SCSI_DATA_REG);
-    status = NCR5380_read(STATUS_REG);
-    mr = NCR5380_read(MODE_REG);
-    icr = NCR5380_read(INITIATOR_COMMAND_REG);
-    basr = NCR5380_read(BUS_AND_STATUS_REG);
-    restore_flags(flags);
-    printk("STATUS_REG: %02x ", status);
-    for (i = 0; signals[i].mask ; ++i) 
-	if (status & signals[i].mask)
-	    printk(",%s", signals[i].name);
-    printk("\nBASR: %02x ", basr);
-    for (i = 0; basrs[i].mask ; ++i) 
-	if (basr & basrs[i].mask)
-	    printk(",%s", basrs[i].name);
-    printk("\nICR: %02x ", icr);
-    for (i = 0; icrs[i].mask; ++i) 
-	if (icr & icrs[i].mask)
-	    printk(",%s", icrs[i].name);
-    printk("\nMODE: %02x ", mr);
-    for (i = 0; mrs[i].mask; ++i) 
-	if (mr & mrs[i].mask)
-	    printk(",%s", mrs[i].name);
-    printk("\n");
-}
-
-static struct {
-    unsigned char value;
-    const char *name;
-} phases[] = {
-    {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
-    {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
-    {PHASE_UNKNOWN, "UNKNOWN"}};
-
-/* 
- * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
- *
- * Purpose : print the current SCSI phase for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print_phase(struct Scsi_Host *instance)
-{
-    unsigned char status;
-    int i;
-
-    status = NCR5380_read(STATUS_REG);
-    if (!(status & SR_REQ)) 
-	printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
-    else {
-	for (i = 0; (phases[i].value != PHASE_UNKNOWN) && 
-	    (phases[i].value != (status & PHASE_MASK)); ++i); 
-	printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
-    }
-}
-
-#else /* !NDEBUG */
-
-/* dummies... */
-__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
-__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };
-
-#endif
-
-/*
- * ++roman: New scheme of calling NCR5380_main()
- * 
- * If we're not in an interrupt, we can call our main directly, it cannot be
- * already running. Else, we queue it on a task queue, if not 'main_running'
- * tells us that a lower level is already executing it. This way,
- * 'main_running' needs not be protected in a special way.
- *
- * queue_main() is a utility function for putting our main onto the task
- * queue, if main_running is false. It should be called only from a
- * interrupt or bottom half.
- */
-
-#include <linux/tqueue.h>
-#include <linux/interrupt.h>
-
-static volatile int main_running = 0;
-static struct tq_struct NCR5380_tqueue = {
-    NULL,		/* next */
-    0,			/* sync */
-    (void (*)(void*))NCR5380_main,  /* routine, must have (void *) arg... */
-    NULL		/* data */
-};
-
-static __inline__ void queue_main(void)
-{
-    if (!main_running) {
-	/* If in interrupt and NCR5380_main() not already running,
-	   queue it on the 'immediate' task queue, to be processed
-	   immediately after the current interrupt processing has
-	   finished. */
-	queue_task(&NCR5380_tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-    }
-    /* else: nothing to do: the running NCR5380_main() will pick up
-       any newly queued command. */
-}
-
-
-static void NCR5380_all_init (void)
-{
-    static int done = 0;
-    if (!done) {
-	INI_PRINTK("scsi : NCR5380_all_init()\n");
-	done = 1;
-    }
-}
-
- 
-/*
- * Function : void NCR58380_print_options (struct Scsi_Host *instance)
- *
- * Purpose : called by probe code indicating the NCR5380 driver
- *	     options that were selected.
- *
- * Inputs : instance, pointer to this instance.  Unused.
- */
-
-static void NCR5380_print_options (struct Scsi_Host *instance)
-{
-    printk(" generic options"
-#ifdef AUTOSENSE 
-    " AUTOSENSE"
-#endif
-#ifdef REAL_DMA
-    " REAL DMA"
-#endif
-#ifdef PSEUDO_DMA
-    " PSEUDO DMA"
-#endif
-#ifdef PARITY
-    " PARITY"
-#endif
-#ifdef SUPPORT_TAGS
-    " SCSI-2 TAGGED QUEUING"
-#endif
-    );
-    printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
-}
-
-/*
- * Function : void NCR5380_print_status (struct Scsi_Host *instance)
- *
- * Purpose : print commands in the various queues, called from
- *	NCR5380_abort and NCR5380_debug to aid debugging.
- *
- * Inputs : instance, pointer to this instance.  
- */
-
-static void NCR5380_print_status (struct Scsi_Host *instance)
-{
-    char *pr_bfr;
-    char *start;
-    int len;
-
-    NCR_PRINT(NDEBUG_ANY);
-    NCR_PRINT_PHASE(NDEBUG_ANY);
-
-    pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
-    if (!pr_bfr) {
-	printk("NCR5380_print_status: no memory for print buffer\n");
-	return;
-    }
-    len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0);
-    pr_bfr[len] = 0;
-    printk("\n%s\n", pr_bfr);
-    free_page((unsigned long) pr_bfr);
-}
-
-
-/******************************************/
-/*
- * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
- *
- * *buffer: I/O buffer
- * **start: if inout == FALSE pointer into buffer where user read should start
- * offset: current offset
- * length: length of buffer
- * hostno: Scsi_Host host_no
- * inout: TRUE - user is writing; FALSE - user is reading
- *
- * Return the number of bytes read from or written
-*/
-
-#undef SPRINTF
-#define SPRINTF(fmt,args...) \
-  do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
-	 pos += sprintf(pos, fmt , ## args); } while(0)
-static
-char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
-
-#ifndef NCR5380_proc_info
-static
-#endif
-int NCR5380_proc_info (char *buffer, char **start, off_t offset,
-		       int length, int hostno, int inout)
-{
-    char *pos = buffer;
-    struct Scsi_Host *instance;
-    struct NCR5380_hostdata *hostdata;
-    Scsi_Cmnd *ptr;
-    unsigned long flags;
-    off_t begin = 0;
-#define check_offset()				\
-    do {					\
-	if (pos - buffer < offset - begin) {	\
-	    begin += pos - buffer;		\
-	    pos = buffer;			\
-	}					\
-    } while (0)
-
-    for (instance = first_instance; instance && HOSTNO != hostno;
-	 instance = instance->next)
-	;
-    if (!instance)
-	return(-ESRCH);
-    hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
-    if (inout) { /* Has data been written to the file ? */
-	return(-ENOSYS);  /* Currently this is a no-op */
-    }
-    SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
-    check_offset();
-    save_flags(flags);
-    cli();
-    SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
-    check_offset();
-    if (!hostdata->connected)
-	SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
-    else
-	pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
-				pos, buffer, length);
-    SPRINTF("scsi%d: issue_queue\n", HOSTNO);
-    check_offset();
-    for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
-	pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
-	check_offset();
-    }
-
-    SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
-    check_offset();
-    for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
-	 ptr = NEXT(ptr)) {
-	pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
-	check_offset();
-    }
-
-    restore_flags(flags);
-    *start = buffer + (offset - begin);
-    if (pos - buffer < offset - begin)
-	return 0;
-    else if (pos - buffer - (offset - begin) < length)
-	return pos - buffer - (offset - begin);
-    return length;
-}
-
-static char *
-lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
-{
-    int i, s;
-    unsigned char *command;
-    SPRINTF("scsi%d: destination target %d, lun %d\n",
-	    H_NO(cmd), cmd->target, cmd->lun);
-    SPRINTF("        command = ");
-    command = cmd->cmnd;
-    SPRINTF("%2d (0x%02x)", command[0], command[0]);
-    for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
-	SPRINTF(" %02x", command[i]);
-    SPRINTF("\n");
-    return pos;
-}
-
-
-/* 
- * Function : void NCR5380_init (struct Scsi_Host *instance)
- *
- * Purpose : initializes *instance and corresponding 5380 chip.
- *
- * Inputs : instance - instantiation of the 5380 driver.  
- *
- * Notes : I assume that the host, hostno, and id bits have been
- * 	set correctly.  I don't care about the irq and other fields. 
- * 
- */
-
-static void NCR5380_init (struct Scsi_Host *instance, int flags)
-{
-    int i;
-    SETUP_HOSTDATA(instance);
-
-    NCR5380_all_init();
-
-    hostdata->aborted = 0;
-    hostdata->id_mask = 1 << instance->this_id;
-    hostdata->id_higher_mask = 0;
-    for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
-	if (i > hostdata->id_mask)
-	    hostdata->id_higher_mask |= i;
-    for (i = 0; i < 8; ++i)
-	hostdata->busy[i] = 0;
-#ifdef SUPPORT_TAGS
-    init_tags();
-#endif
-#if defined (REAL_DMA)
-    hostdata->dma_len = 0;
-#endif
-    hostdata->targets_present = 0;
-    hostdata->connected = NULL;
-    hostdata->issue_queue = NULL;
-    hostdata->disconnected_queue = NULL;
-    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
-
-    if (!the_template) {
-	the_template = instance->hostt;
-	first_instance = instance;
-    }
-	
-
-#ifndef AUTOSENSE
-    if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
-	 printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
-	        "        without AUTOSENSE option, contingent allegiance conditions may\n"
-	        "        be incorrectly cleared.\n", HOSTNO);
-#endif /* def AUTOSENSE */
-
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-    NCR5380_write(MODE_REG, MR_BASE);
-    NCR5380_write(TARGET_COMMAND_REG, 0);
-    NCR5380_write(SELECT_ENABLE_REG, 0);
-}
-
-/* 
- * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, 
- *	void (*done)(Scsi_Cmnd *)) 
- *
- * Purpose :  enqueues a SCSI command
- *
- * Inputs : cmd - SCSI command, done - function called on completion, with
- *	a pointer to the command descriptor.
- * 
- * Returns : 0
- *
- * Side effects : 
- *      cmd is added to the per instance issue_queue, with minor 
- *	twiddling done to the host specific fields of cmd.  If the 
- *	main coroutine is not running, it is restarted.
- *
- */
-
-/* Only make static if a wrapper function is used */
-#ifndef NCR5380_queue_command
-static
-#endif
-int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
-{
-    SETUP_HOSTDATA(cmd->host);
-    Scsi_Cmnd *tmp;
-    int oldto;
-    unsigned long flags;
-    extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
-
-#if (NDEBUG & NDEBUG_NO_WRITE)
-    switch (cmd->cmnd[0]) {
-    case WRITE_6:
-    case WRITE_10:
-	printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
-	       H_NO(cmd));
-	cmd->result = (DID_ERROR << 16);
-	done(cmd);
-	return 0;
-    }
-#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-
-
-#ifdef NCR5380_STATS
-# if 0
-    if (!hostdata->connected && !hostdata->issue_queue &&
-	!hostdata->disconnected_queue) {
-	hostdata->timebase = jiffies;
-    }
-# endif
-# ifdef NCR5380_STAT_LIMIT
-    if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
-# endif
-	switch (cmd->cmnd[0])
-	{
-	    case WRITE:
-	    case WRITE_6:
-	    case WRITE_10:
-		hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase);
-		hostdata->bytes_write[cmd->target] += cmd->request_bufflen;
-		hostdata->pendingw++;
-		break;
-	    case READ:
-	    case READ_6:
-	    case READ_10:
-		hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase);
-		hostdata->bytes_read[cmd->target] += cmd->request_bufflen;
-		hostdata->pendingr++;
-		break;
-	}
-#endif
-
-    /* 
-     * We use the host_scribble field as a pointer to the next command  
-     * in a queue 
-     */
-
-    NEXT(cmd) = NULL;
-    cmd->scsi_done = done;
-
-    cmd->result = 0;
-
-
-    /* 
-     * Insert the cmd into the issue queue. Note that REQUEST SENSE 
-     * commands are added to the head of the queue since any command will
-     * clear the contingent allegiance condition that exists and the 
-     * sense data is only guaranteed to be valid while the condition exists.
-     */
-
-    save_flags(flags);
-    cli();
-
-    if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
-	LIST(cmd, hostdata->issue_queue);
-	NEXT(cmd) = hostdata->issue_queue;
-	hostdata->issue_queue = cmd;
-    } else {
-	for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
-	     NEXT(tmp); tmp = NEXT(tmp))
-	    ;
-	LIST(cmd, tmp);
-	NEXT(tmp) = cmd;
-    }
-    restore_flags(flags);
-
-    QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
-	      (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
-
-    /* If queue_command() is called from an interrupt (real one or bottom
-     * half), we let queue_main() do the job of taking care about main. If it
-     * is already running, this is a no-op, else main will be queued.
-     *
-     * If we're not in an interrupt, we can call NCR5380_main()
-     * unconditionally, because it cannot be already running.
-     */
-    if (in_interrupt() > 0 || ((flags >> 8) & 7) >= 6)
-	queue_main();
-    else
-	NCR5380_main();
-    return 0;
-}
-
-/*
- * Function : NCR5380_main (void) 
- *
- * Purpose : NCR5380_main is a coroutine that runs as long as more work can 
- *	be done on the NCR5380 host adapters in a system.  Both 
- *	NCR5380_queue_command() and NCR5380_intr() will try to start it 
- *	in case it is not running.
- * 
- * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should 
- *  reenable them.  This prevents reentrancy and kernel stack overflow.
- */ 	
-    
-static void NCR5380_main (void)
-{
-    Scsi_Cmnd *tmp, *prev;
-    struct Scsi_Host *instance = first_instance;
-    struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
-    int done;
-    unsigned long flags;
-    
-    /*
-     * We run (with interrupts disabled) until we're sure that none of 
-     * the host adapters have anything that can be done, at which point 
-     * we set main_running to 0 and exit.
-     *
-     * Interrupts are enabled before doing various other internal 
-     * instructions, after we've decided that we need to run through
-     * the loop again.
-     *
-     * this should prevent any race conditions.
-     * 
-     * ++roman: Just disabling the NCR interrupt isn't sufficient here,
-     * because also a timer int can trigger an abort or reset, which can
-     * alter queues and touch the Falcon lock.
-     */
-
-    /* Tell int handlers main() is now already executing.  Note that
-       no races are possible here. If an int comes in before
-       'main_running' is set here, and queues/executes main via the
-       task queue, it doesn't do any harm, just this instance of main
-       won't find any work left to do. */
-    if (main_running)
-    	return;
-    main_running = 1;
-
-    save_flags(flags);
-    do {
-	cli(); /* Freeze request queues */
-	done = 1;
-	
-	if (!hostdata->connected) {
-	    MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO );
-	    /*
-	     * Search through the issue_queue for a command destined
-	     * for a target that's not busy.
-	     */
-#if (NDEBUG & NDEBUG_LISTS)
-	    for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
-		 tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
-		;
-		/*printk("%p  ", tmp);*/
-	    if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
-#endif
-	    for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, 
-		 prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
-
-#if (NDEBUG & NDEBUG_LISTS)
-		if (prev != tmp)
-		    printk("MAIN tmp=%p   target=%d   busy=%d lun=%d\n",
-			   tmp, tmp->target, hostdata->busy[tmp->target],
-			   tmp->lun);
-#endif
-		/*  When we find one, remove it from the issue queue. */
-		if (
-#ifdef SUPPORT_TAGS
-		    !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
-#else
-		    !(hostdata->busy[tmp->target] & (1 << tmp->lun))
-#endif
-		    ) {
-		    cli(); /* ++guenther: just to be sure, this must be atomic */
-		    if (prev) {
-		        REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
-			NEXT(prev) = NEXT(tmp);
-		    } else {
-		        REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
-			hostdata->issue_queue = NEXT(tmp);
-		    }
-		    NEXT(tmp) = NULL;
-		    
-		    /* reenable interrupts after finding one */
-		    restore_flags(flags);
-		    
-		    /* 
-		     * Attempt to establish an I_T_L nexus here. 
-		     * On success, instance->hostdata->connected is set.
-		     * On failure, we must add the command back to the
-		     *   issue queue so we can keep trying.	
-		     */
-		    MAIN_PRINTK("scsi%d: main(): command for target %d "
-				"lun %d removed from issue_queue\n",
-				HOSTNO, tmp->target, tmp->lun);
-		    /* 
-		     * REQUEST SENSE commands are issued without tagged
-		     * queueing, even on SCSI-II devices because the 
-		     * contingent allegiance condition exists for the 
-		     * entire unit.
-		     */
-		    /* ++roman: ...and the standard also requires that
-		     * REQUEST SENSE command are untagged.
-		     */
-		    
-#ifdef SUPPORT_TAGS
-		    cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
-#endif
-		    if (!NCR5380_select(instance, tmp, 
-			    (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : 
-			    TAG_NEXT)) {
-			break;
-		    } else {
-			cli();
-			LIST(tmp, hostdata->issue_queue);
-			NEXT(tmp) = hostdata->issue_queue;
-			hostdata->issue_queue = tmp;
-#ifdef SUPPORT_TAGS
-			cmd_free_tag( tmp );
-#endif
-			restore_flags(flags);
-			MAIN_PRINTK("scsi%d: main(): select() failed, "
-				    "returned to issue_queue\n", HOSTNO);
-			if (hostdata->connected)
-			    break;
-		    }
-		} /* if target/lun/target queue is not busy */
-	    } /* for issue_queue */
-	} /* if (!hostdata->connected) */
-		
-	if (hostdata->connected 
-#ifdef REAL_DMA
-	    && !hostdata->dma_len
-#endif
-	    ) {
-	    restore_flags(flags);
-	    MAIN_PRINTK("scsi%d: main: performing information transfer\n",
-			HOSTNO);
-	    NCR5380_information_transfer(instance);
-	    MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
-	    done = 0;
-	}
-    } while (!done);
-
-    /* Better allow ints _after_ 'main_running' has been cleared, else
-       an interrupt could believe we'll pick up the work it left for
-       us, but we won't see it anymore here... */
-    main_running = 0;
-    restore_flags(flags);
-}
-
-
-#ifdef REAL_DMA
-/*
- * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
- *
- * Purpose : Called by interrupt handler when DMA finishes or a phase
- *	mismatch occurs (which would finish the DMA transfer).  
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-static void NCR5380_dma_complete( struct Scsi_Host *instance )
-{
-    SETUP_HOSTDATA(instance);
-    int           transfered, saved_data = 0, overrun = 0, cnt, toPIO;
-    unsigned char **data, p;
-    volatile int  *count;
-
-    if (!hostdata->connected) {
-	printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
-	       "no connected cmd\n", HOSTNO);
-	return;
-    }
-    
-    if (mac_read_overruns) {
-	p = hostdata->connected->SCp.phase;
-	if (p & SR_IO) {
-	    udelay(10);
-	    if ((((NCR5380_read(BUS_AND_STATUS_REG)) &
-		  (BASR_PHASE_MATCH|BASR_ACK)) ==
-		 (BASR_PHASE_MATCH|BASR_ACK))) {
-		saved_data = NCR5380_read(INPUT_DATA_REG);
-		overrun = 1;
-		DMA_PRINTK("scsi%d: read overrun handled\n", HOSTNO);
-	    }
-	}
-    }
-
-    DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
-	       HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
-	       NCR5380_read(STATUS_REG));
-
-    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-    NCR5380_write(MODE_REG, MR_BASE);
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-    transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
-    hostdata->dma_len = 0;
-
-    data = (unsigned char **) &(hostdata->connected->SCp.ptr);
-    count = &(hostdata->connected->SCp.this_residual);
-    *data += transfered;
-    *count -= transfered;
-
-    if (mac_read_overruns) {
-	if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
-	    cnt = toPIO = mac_read_overruns;
-	    if (overrun) {
-		DMA_PRINTK("Got an input overrun, using saved byte\n");
-		*(*data)++ = saved_data;
-		(*count)--;
-		cnt--;
-		toPIO--;
-	    }
-	    DMA_PRINTK("Doing %d-byte PIO to 0x%08lx\n", cnt, (long)*data);
-	    NCR5380_transfer_pio(instance, &p, &cnt, data);
-	    *count -= toPIO - cnt;
-	}
-    }
-}
-#endif /* REAL_DMA */
-
-
-/*
- * Function : void NCR5380_intr (int irq)
- * 
- * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- *	from the disconnected queue, and restarting NCR5380_main() 
- *	as required.
- *
- * Inputs : int irq, irq that caused this interrupt.
- *
- */
-
-static void NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs)
-{
-    struct Scsi_Host *instance = first_instance;
-    int done = 1;
-    unsigned char basr;
-
-    INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
-
-    /* Look for pending interrupts */
-    basr = NCR5380_read(BUS_AND_STATUS_REG);
-    INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
-    /* dispatch to appropriate routine if found and done=0 */
-    if (basr & BASR_IRQ) {
-	NCR_PRINT(NDEBUG_INTR);
-	if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
-	    done = 0;
-	    ENABLE_IRQ();
-	    INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
-	    NCR5380_reselect(instance);
-	    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-	}
-	else if (basr & BASR_PARITY_ERROR) {
-	    INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
-	    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-	}
-	else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
-	    INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
-	    (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-	}
-	else {
-	    /*  
-	     * The rest of the interrupt conditions can occur only during a
-	     * DMA transfer
-	     */
-
-#if defined(REAL_DMA)
-	    /*
-	     * We should only get PHASE MISMATCH and EOP interrupts if we have
-	     * DMA enabled, so do a sanity check based on the current setting
-	     * of the MODE register.
-	     */
-
-	    if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
-		((basr & BASR_END_DMA_TRANSFER) || 
-		 !(basr & BASR_PHASE_MATCH))) {
-		    
-		INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
-		NCR5380_dma_complete( instance );
-		done = 0;
-		ENABLE_IRQ();
-	    } else
-#endif /* REAL_DMA */
-	    {
-/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
-		if (basr & BASR_PHASE_MATCH)
-		    printk(KERN_NOTICE "scsi%d: unknown interrupt, "
-			   "BASR 0x%x, MR 0x%x, SR 0x%x\n",
-			   HOSTNO, basr, NCR5380_read(MODE_REG),
-			   NCR5380_read(STATUS_REG));
-		(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-	    }
-	} /* if !(SELECTION || PARITY) */
-    } /* BASR & IRQ */
-    else {
-	printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
-	       "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
-	       NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
-	(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-    }
-    
-    if (!done) {
-	INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
-	/* Put a call to NCR5380_main() on the queue... */
-	queue_main();
-    }
-}
-
-#ifdef NCR5380_STATS
-static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
-{
-# ifdef NCR5380_STAT_LIMIT
-    if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
-# endif
-	switch (cmd->cmnd[0])
-	{
-	    case WRITE:
-	    case WRITE_6:
-	    case WRITE_10:
-		hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase);
-		/*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/
-		hostdata->pendingw--;
-		break;
-	    case READ:
-	    case READ_6:
-	    case READ_10:
-		hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase);
-		/*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/
-		hostdata->pendingr--;
-		break;
-	}
-}
-#endif
-
-/* 
- * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, 
- *	int tag);
- *
- * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- *	including ARBITRATION, SELECTION, and initial message out for 
- *	IDENTIFY and queue messages. 
- *
- * Inputs : instance - instantiation of the 5380 driver on which this 
- * 	target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for 
- *	new tag, TAG_NONE for untagged queueing, otherwise set to the tag for 
- *	the command that is presently connected.
- * 
- * Returns : -1 if selection could not execute for some reason,
- *	0 if selection succeeded or failed because the target 
- * 	did not respond.
- *
- * Side effects : 
- * 	If bus busy, arbitration failed, etc, NCR5380_select() will exit 
- *		with registers as they should have been on entry - ie
- *		SELECT_ENABLE will be set appropriately, the NCR5380
- *		will cease to drive any SCSI bus signals.
- *
- *	If successful : I_T_L or I_T_L_Q nexus will be established, 
- *		instance->connected will be set to cmd.  
- * 		SELECT interrupt will be disabled.
- *
- *	If failed (no target) : cmd->scsi_done() will be called, and the 
- *		cmd->result host byte set to DID_BAD_TARGET.
- */
-
-static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
-{
-    SETUP_HOSTDATA(instance);
-    unsigned char tmp[3], phase;
-    unsigned char *data;
-    int len;
-    unsigned long timeout;
-    unsigned long flags;
-
-    hostdata->restart_select = 0;
-    NCR_PRINT(NDEBUG_ARBITRATION);
-    ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
-	       instance->this_id);
-
-    /* 
-     * Set the phase bits to 0, otherwise the NCR5380 won't drive the 
-     * data bus during SELECTION.
-     */
-
-    save_flags(flags);
-    cli();
-    if (hostdata->connected) {
-	restore_flags(flags);
-	return -1;
-    }
-    NCR5380_write(TARGET_COMMAND_REG, 0);
-
-
-    /* 
-     * Start arbitration.
-     */
-    
-    NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
-    NCR5380_write(MODE_REG, MR_ARBITRATE);
-
-    restore_flags(flags);
-
-    /* Wait for arbitration logic to complete */
-#if NCR_TIMEOUT
-    {
-      unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
-
-      while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
-	   && time_before(jiffies, timeout) && !hostdata->connected)
-	;
-      if (time_after_eq(jiffies, timeout))
-      {
-	printk("scsi : arbitration timeout at %d\n", __LINE__);
-	NCR5380_write(MODE_REG, MR_BASE);
-	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-	return -1;
-      }
-    }
-#else /* NCR_TIMEOUT */
-    while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
-	 && !hostdata->connected);
-#endif
-
-    ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
-
-    if (hostdata->connected) {
-	NCR5380_write(MODE_REG, MR_BASE); 
-	return -1;
-    }
-    /* 
-     * The arbitration delay is 2.2us, but this is a minimum and there is 
-     * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
-     * the integral nature of udelay().
-     *
-     */
-
-    udelay(3);
-
-    /* Check for lost arbitration */
-    if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
-	(NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
-	(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
-	hostdata->connected) {
-	NCR5380_write(MODE_REG, MR_BASE); 
-	ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
-		   HOSTNO);
-	return -1;
-    }
-
-     /* after/during arbitration, BSY should be asserted.
-	IBM DPES-31080 Version S31Q works now */
-     /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
-					 ICR_ASSERT_BSY ) ;
-    
-    if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
-	hostdata->connected) {
-	NCR5380_write(MODE_REG, MR_BASE);
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
-		   HOSTNO);
-	return -1;
-    }
-
-    /* 
-     * Again, bus clear + bus settle time is 1.2us, however, this is 
-     * a minimum so we'll udelay ceil(1.2)
-     */
-
-#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
-    /* ++roman: But some targets (see above :-) seem to need a bit more... */
-    udelay(15);
-#else
-    udelay(2);
-#endif
-    
-    if (hostdata->connected) {
-	NCR5380_write(MODE_REG, MR_BASE);
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	return -1;
-    }
-
-    ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
-
-    /* 
-     * Now that we have won arbitration, start Selection process, asserting 
-     * the host and target ID's on the SCSI bus.
-     */
-
-    NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
-
-    /* 
-     * Raise ATN while SEL is true before BSY goes false from arbitration,
-     * since this is the only way to guarantee that we'll get a MESSAGE OUT
-     * phase immediately after selection.
-     */
-
-    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | 
-	ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
-    NCR5380_write(MODE_REG, MR_BASE);
-
-    /* 
-     * Reselect interrupts must be turned off prior to the dropping of BSY,
-     * otherwise we will trigger an interrupt.
-     */
-
-    if (hostdata->connected) {
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	return -1;
-    }
-
-    NCR5380_write(SELECT_ENABLE_REG, 0);
-
-    /*
-     * The initiator shall then wait at least two deskew delays and release 
-     * the BSY signal.
-     */
-    udelay(1);        /* wingel -- wait two bus deskew delay >2*45ns */
-
-    /* Reset BSY */
-    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | 
-	ICR_ASSERT_ATN | ICR_ASSERT_SEL));
-
-    /* 
-     * Something weird happens when we cease to drive BSY - looks
-     * like the board/chip is letting us do another read before the 
-     * appropriate propagation delay has expired, and we're confusing
-     * a BSY signal from ourselves as the target's response to SELECTION.
-     *
-     * A small delay (the 'C++' frontend breaks the pipeline with an
-     * unnecessary jump, making it work on my 386-33/Trantor T128, the
-     * tighter 'C' code breaks and requires this) solves the problem - 
-     * the 1 us delay is arbitrary, and only used because this delay will 
-     * be the same on other platforms and since it works here, it should 
-     * work there.
-     *
-     * wingel suggests that this could be due to failing to wait
-     * one deskew delay.
-     */
-
-    udelay(1);
-
-    SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->target);
-
-    /* 
-     * The SCSI specification calls for a 250 ms timeout for the actual 
-     * selection.
-     */
-
-    timeout = jiffies + 25; 
-
-    /* 
-     * XXX very interesting - we're seeing a bounce where the BSY we 
-     * asserted is being reflected / still asserted (propagation delay?)
-     * and it's detecting as true.  Sigh.
-     */
-
-#if 0
-    /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
-     * IO while SEL is true. But again, there are some disks out the in the
-     * world that do that nevertheless. (Somebody claimed that this announces
-     * reselection capability of the target.) So we better skip that test and
-     * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
-     */
-
-    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & 
-	(SR_BSY | SR_IO)));
-
-    if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
-	    (SR_SEL | SR_IO)) {
-	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	    NCR5380_reselect(instance);
-	    printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
-		    HOSTNO);
-	    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-	    return -1;
-    }
-#else
-    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
-#endif
-
-    /* 
-     * No less than two deskew delays after the initiator detects the 
-     * BSY signal is true, it shall release the SEL signal and may 
-     * change the DATA BUS.                                     -wingel
-     */
-
-    udelay(1);
-
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
-    if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	if (hostdata->targets_present & (1 << cmd->target)) {
-	    printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
-	    if (hostdata->restart_select)
-		printk(KERN_NOTICE "\trestart select\n");
-	    NCR_PRINT(NDEBUG_ANY);
-	    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-	    return -1;
-	}
-	cmd->result = DID_BAD_TARGET << 16;
-#ifdef NCR5380_STATS
-	collect_stats(hostdata, cmd);
-#endif
-#ifdef SUPPORT_TAGS
-	cmd_free_tag( cmd );
-#endif
-	cmd->scsi_done(cmd);
-	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-	SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
-	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-	return 0;
-    } 
-
-    hostdata->targets_present |= (1 << cmd->target);
-
-    /*
-     * Since we followed the SCSI spec, and raised ATN while SEL 
-     * was true but before BSY was false during selection, the information
-     * transfer phase should be a MESSAGE OUT phase so that we can send the
-     * IDENTIFY message.
-     * 
-     * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
-     * message (2 bytes) with a tag ID that we increment with every command
-     * until it wraps back to 0.
-     *
-     * XXX - it turns out that there are some broken SCSI-II devices,
-     *	     which claim to support tagged queuing but fail when more than
-     *	     some number of commands are issued at once.
-     */
-
-    /* Wait for start of REQ/ACK handshake */
-    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
-
-    SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
-	       HOSTNO, cmd->target);
-    tmp[0] = IDENTIFY(1, cmd->lun);
-
-#ifdef SUPPORT_TAGS
-    if (cmd->tag != TAG_NONE) {
-	tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
-	tmp[2] = cmd->tag;
-	len = 3;
-    } else 
-	len = 1;
-#else
-    len = 1;
-    cmd->tag=0;
-#endif /* SUPPORT_TAGS */
-
-    /* Send message(s) */
-    data = tmp;
-    phase = PHASE_MSGOUT;
-    NCR5380_transfer_pio(instance, &phase, &len, &data);
-    SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
-    /* XXX need to handle errors here */
-    hostdata->connected = cmd;
-#ifndef SUPPORT_TAGS
-    hostdata->busy[cmd->target] |= (1 << cmd->lun);
-#endif    
-
-    initialize_SCp(cmd);
-
-
-    return 0;
-}
-
-/* 
- * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, 
- *      unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using polled I/O
- *
- * Inputs : instance - instance of driver, *phase - pointer to 
- *	what phase is expected, *count - pointer to number of 
- *	bytes to transfer, **data - pointer to data pointer.
- * 
- * Returns : -1 when different phase is entered without transferring
- *	maximum number of bytes, 0 if all bytes are transfered or exit
- *	is in same phase.
- *
- * 	Also, *phase, *count, *data are modified in place.
- *
- * XXX Note : handling for bus free may be useful.
- */
-
-/*
- * Note : this code is not as quick as it could be, however it 
- * IS 100% reliable, and for the actual data transfer where speed
- * counts, we will always do a pseudo DMA or DMA transfer.
- */
-
-static int NCR5380_transfer_pio( struct Scsi_Host *instance, 
-				 unsigned char *phase, int *count,
-				 unsigned char **data)
-{
-    register unsigned char p = *phase, tmp;
-    register int c = *count;
-    register unsigned char *d = *data;
-
-    /* 
-     * The NCR5380 chip will only drive the SCSI bus when the 
-     * phase specified in the appropriate bits of the TARGET COMMAND
-     * REGISTER match the STATUS REGISTER
-     */
-
-    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-
-    do {
-	/* 
-	 * Wait for assertion of REQ, after which the phase bits will be 
-	 * valid 
-	 */
-	while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
-
-	HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
-
-	/* Check for phase mismatch */	
-	if ((tmp & PHASE_MASK) != p) {
-	    PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
-	    NCR_PRINT_PHASE(NDEBUG_PIO);
-	    break;
-	}
-
-	/* Do actual transfer from SCSI bus to / from memory */
-	if (!(p & SR_IO)) 
-	    NCR5380_write(OUTPUT_DATA_REG, *d);
-	else 
-	    *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
-
-	++d;
-
-	/* 
-	 * The SCSI standard suggests that in MSGOUT phase, the initiator
-	 * should drop ATN on the last byte of the message phase
-	 * after REQ has been asserted for the handshake but before
-	 * the initiator raises ACK.
-	 */
-
-	if (!(p & SR_IO)) {
-	    if (!((p & SR_MSG) && c > 1)) {
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-		    ICR_ASSERT_DATA);
-		NCR_PRINT(NDEBUG_PIO);
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-			ICR_ASSERT_DATA | ICR_ASSERT_ACK);
-	    } else {
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
-		    ICR_ASSERT_DATA | ICR_ASSERT_ATN);
-		NCR_PRINT(NDEBUG_PIO);
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-		    ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
-	    }
-	} else {
-	    NCR_PRINT(NDEBUG_PIO);
-	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
-	}
-
-	while (NCR5380_read(STATUS_REG) & SR_REQ);
-
-	HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
-
-/*
- * We have several special cases to consider during REQ/ACK handshaking : 
- * 1.  We were in MSGOUT phase, and we are on the last byte of the 
- *	message.  ATN must be dropped as ACK is dropped.
- *
- * 2.  We are in a MSGIN phase, and we are on the last byte of the  
- *	message.  We must exit with ACK asserted, so that the calling
- *	code may raise ATN before dropping ACK to reject the message.
- *
- * 3.  ACK and ATN are clear and the target may proceed as normal.
- */
-	if (!(p == PHASE_MSGIN && c == 1)) {  
-	    if (p == PHASE_MSGOUT && c > 1)
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-	    else
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	} 
-    } while (--c);
-
-    PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
-
-    *count = c;
-    *data = d;
-    tmp = NCR5380_read(STATUS_REG);
-    /* The phase read from the bus is valid if either REQ is (already)
-     * asserted or if ACK hasn't been released yet. The latter is the case if
-     * we're in MSGIN and all wanted bytes have been received. */
-    if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
-	*phase = tmp & PHASE_MASK;
-    else 
-	*phase = PHASE_UNKNOWN;
-
-    if (!c || (*phase == p))
-	return 0;
-    else 
-	return -1;
-}
-
-/*
- * Function : do_abort (Scsi_Host *host)
- * 
- * Purpose : abort the currently established nexus.  Should only be 
- * 	called from a routine which can drop into a 
- * 
- * Returns : 0 on success, -1 on failure.
- */
-
-static int do_abort (struct Scsi_Host *host) 
-{
-    unsigned char tmp, *msgptr, phase;
-    int len;
-
-    /* Request message out phase */
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
-    /* 
-     * Wait for the target to indicate a valid phase by asserting 
-     * REQ.  Once this happens, we'll have either a MSGOUT phase 
-     * and can immediately send the ABORT message, or we'll have some 
-     * other phase and will have to source/sink data.
-     * 
-     * We really don't care what value was on the bus or what value
-     * the target sees, so we just handshake.
-     */
-    
-    while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
-
-    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
-    if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
-		      ICR_ASSERT_ACK);
-	while (NCR5380_read(STATUS_REG) & SR_REQ);
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-    }
-   
-    tmp = ABORT;
-    msgptr = &tmp;
-    len = 1;
-    phase = PHASE_MSGOUT;
-    NCR5380_transfer_pio (host, &phase, &len, &msgptr);
-
-    /*
-     * If we got here, and the command completed successfully,
-     * we're about to go into bus free state.
-     */
-
-    return len ? -1 : 0;
-}
-
-#if defined(REAL_DMA) || defined(PSEUDO_DMA)
-/* 
- * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, 
- *      unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using either real
- *	or pseudo DMA.
- *
- * Inputs : instance - instance of driver, *phase - pointer to 
- *	what phase is expected, *count - pointer to number of 
- *	bytes to transfer, **data - pointer to data pointer.
- * 
- * Returns : -1 when different phase is entered without transferring
- *	maximum number of bytes, 0 if all bytes or transfered or exit
- *	is in same phase.
- *
- * 	Also, *phase, *count, *data are modified in place.
- *
- */
-
-
-static int NCR5380_transfer_dma( struct Scsi_Host *instance, 
-				 unsigned char *phase, int *count,
-				 unsigned char **data)
-{
-    SETUP_HOSTDATA(instance);
-    register int c = *count;
-    register unsigned char p = *phase;
-    register unsigned char *d = *data;
-    register int foo;
-    unsigned char tmp;
-    unsigned long flags;
-
-
-    if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
-        *phase = tmp;
-        return -1;
-    }
-
-    if (mac_read_overruns && (p & SR_IO)) {
-	c -= mac_read_overruns;
-    }
-
-    DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
-	       HOSTNO, (p & SR_IO) ? "reading" : "writing",
-	       c, (p & SR_IO) ? "to" : "from", d);
-
-    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-
-#ifdef REAL_DMA
-    NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
-#else /* PSEUDO_DMA! */
-#if defined(PSEUDO_DMA) && !defined(UNSAFE)
-    save_flags(flags);
-    cli();
-#endif
-    /* KLL May need eop and parity in 53c400 */
-    if (hostdata->flags & FLAG_NCR53C400)
-	NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK
-	| MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE
-	| MR_MONITOR_BSY);
-    else
-#ifndef EMULATE_PSEUDO_DMA
-	NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
-#else
-        NCR5380_write(MODE_REG, MR_BASE);
-#endif
-#endif /* def REAL_DMA  */
-
-#ifdef REAL_DMA
-    /* On the Medusa, it is a must to initialize the DMA before
-     * starting the NCR. This is also the cleaner way for the TT.
-     */
-    save_flags(flags);
-    cli();
-    hostdata->dma_len = (p & SR_IO) ?
-        NCR5380_dma_read_setup(instance, d, c) : 
-        NCR5380_dma_write_setup(instance, d, c);
-    restore_flags(flags);
-#endif /* def REAL_DMA */
-
-#ifndef EMULATE_PSEUDO_DMA
-    if (p & SR_IO)
-	NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
-    else {
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
-	NCR5380_write(START_DMA_SEND_REG, 0);
-    }
-#else
-    hostdata->dma_len = c;
-#endif
-
-#if defined(REAL_DMA)
-    return 0;
-#else /* defined(PSEUDO_DMA) */
-    if (p & SR_IO) {
-#ifdef DMA_WORKS_RIGHT
-        foo = NCR5380_pread(instance, d, c);
-#else
-	int diff = 1;
-	if (hostdata->flags & FLAG_NCR53C400) {
-	    diff=0;
-	}
-
-	if (!(foo = NCR5380_pread(instance, d, c - diff))) {
-	    /*
-	     * We can't disable DMA mode after successfully transferring 
-	     * what we plan to be the last byte, since that would open up
-	     * a race condition where if the target asserted REQ before 
-	     * we got the DMA mode reset, the NCR5380 would have latched
-	     * an additional byte into the INPUT DATA register and we'd
-	     * have dropped it.
-	     * 
-	     * The workaround was to transfer one fewer bytes than we 
-	     * intended to with the pseudo-DMA read function, wait for 
-	     * the chip to latch the last byte, read it, and then disable
-	     * pseudo-DMA mode.
-	     * 
-	     * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
-	     * REQ is deasserted when ACK is asserted, and not reasserted
-	     * until ACK goes false.  Since the NCR5380 won't lower ACK
-	     * until DACK is asserted, which won't happen unless we twiddle
-	     * the DMA port or we take the NCR5380 out of DMA mode, we 
-	     * can guarantee that we won't handshake another extra 
-	     * byte.
-    	     */
-
-	    if (!(hostdata->flags & FLAG_NCR53C400)) {
-		while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
-		/* Wait for clean handshake */
-		while (NCR5380_read(STATUS_REG) & SR_REQ);
-		d[c - 1] = NCR5380_read(INPUT_DATA_REG);
-	    }
-	}
-#endif
-    } else {
-#ifdef DMA_WORKS_RIGHT
-        foo = NCR5380_pwrite(instance, d, c);
-#else
-        int timeout;
-#if (NDEBUG & NDEBUG_C400_PWRITE)
-	printk("About to pwrite %d bytes\n", c);
-#endif
-	if (!(foo = NCR5380_pwrite(instance, d, c))) {
-	    /*
-	     * Wait for the last byte to be sent.  If REQ is being asserted for 
-	     * the byte we're interested, we'll ACK it and it will go false.  
-	     */
-	    if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
-		timeout = 20000;
-#if 1
-#if 1
-		while (!(NCR5380_read(BUS_AND_STATUS_REG) & 
-			BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
-			BASR_PHASE_MATCH));
-#else
-		if (NCR5380_read(STATUS_REG) & SR_REQ) {
-		    for (; timeout && 
-			!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); 
-			--timeout);
-		    for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
-			--timeout);
-		} 
-#endif
-	
-
-#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
-		if (!timeout) 
-		    printk("scsi%d : timed out on last byte\n",
-			    instance->host_no);
-#endif
-
-
-		if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
-		    hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
-		    if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
-			hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
-#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
-			printk("scsi%d : last bit sent works\n", 
-			    instance->host_no);
-#endif
-		    }
-		}
-	    } else  {
-#if (NDEBUG & NDEBUG_C400_PWRITE)
-		printk("Waiting for LASTBYTE\n");
-#endif
-		while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
-#if (NDEBUG & NDEBUG_C400_PWRITE)
-		printk("Got LASTBYTE\n");
-#endif
-	    }
-#else
-	    udelay (5);
-#endif
-	}
-#endif
-    }
-
-    NCR5380_write(MODE_REG, MR_BASE);
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-    if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
-#if (NDEBUG & NDEBUG_C400_PWRITE)
-	printk("53C400w: Checking for IRQ\n");
-#endif
-	if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
-#if (NDEBUG & NDEBUG_C400_PWRITE)
-	    printk("53C400w:    got it, reading reset interrupt reg\n");
-#endif
-	    NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-	} else {
-	    printk("53C400w:    IRQ NOT THERE!\n");
-	}
-    }
-
-    *data = d + c;
-    *count = 0;
-    *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
-#if 0
-    NCR5380_print_phase(instance);
-#endif
-#if defined(PSEUDO_DMA) && !defined(UNSAFE)
-    restore_flags(flags);
-#endif /* defined(REAL_DMA_POLL) */
-    return foo;
-#endif /* def REAL_DMA */
-}
-#endif /* defined(REAL_DMA) || defined(PSEUDO_DMA) */
-
-
-/*
- * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
- *
- * Purpose : run through the various SCSI phases and do as the target 
- * 	directs us to.  Operates on the currently connected command, 
- *	instance->connected.
- *
- * Inputs : instance, instance for which we are doing commands
- *
- * Side effects : SCSI things happen, the disconnected queue will be 
- *	modified if a command disconnects, *instance->connected will
- *	change.
- *
- * XXX Note : we need to watch for bus free or a reset condition here 
- * 	to recover from an unexpected bus free condition.
- */
- 
-static void NCR5380_information_transfer (struct Scsi_Host *instance)
-{
-    SETUP_HOSTDATA(instance);
-    unsigned long flags;
-    unsigned char msgout = NOP;
-    int sink = 0;
-    int len;
-#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
-    int transfersize;
-#endif
-    unsigned char *data;
-    unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
-    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
-
-    while (1) {
-	tmp = NCR5380_read(STATUS_REG);
-	/* We only have a valid SCSI phase when REQ is asserted */
-	if (tmp & SR_REQ) {
-	    phase = (tmp & PHASE_MASK); 
-	    if (phase != old_phase) {
-		old_phase = phase;
-		NCR_PRINT_PHASE(NDEBUG_INFORMATION);
-	    }
-	    
-	    if (sink && (phase != PHASE_MSGOUT)) {
-		NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
-		    ICR_ASSERT_ACK);
-		while (NCR5380_read(STATUS_REG) & SR_REQ);
-		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-		    ICR_ASSERT_ATN);
-		sink = 0;
-		continue;
-	    }
-
-	    switch (phase) {
-	    case PHASE_DATAOUT:
-#if (NDEBUG & NDEBUG_NO_DATAOUT)
-		printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
-		       "aborted\n", HOSTNO);
-		sink = 1;
-		do_abort(instance);
-		cmd->result = DID_ERROR  << 16;
-		cmd->done(cmd);
-		return;
-#endif
-	    case PHASE_DATAIN:
-		/* 
-		 * If there is no room left in the current buffer in the
-		 * scatter-gather list, move onto the next one.
-		 */
-
-		if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
-		    ++cmd->SCp.buffer;
-		    --cmd->SCp.buffers_residual;
-		    cmd->SCp.this_residual = cmd->SCp.buffer->length;
-		    cmd->SCp.ptr = cmd->SCp.buffer->address;
-		    /* ++roman: Try to merge some scatter-buffers if
-		     * they are at contiguous physical addresses.
-		     */
-		    merge_contiguous_buffers( cmd );
-		    INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
-			       HOSTNO, cmd->SCp.this_residual,
-			       cmd->SCp.buffers_residual);
-		}
-
-		/*
-		 * The preferred transfer method is going to be 
-		 * PSEUDO-DMA for systems that are strictly PIO,
-		 * since we can let the hardware do the handshaking.
-		 *
-		 * For this to work, we need to know the transfersize
-		 * ahead of time, since the pseudo-DMA code will sit
-		 * in an unconditional loop.
-		 */
-
-/* ++roman: I suggest, this should be
- *   #if def(REAL_DMA)
- * instead of leaving REAL_DMA out.
- */
-
-#if defined(REAL_DMA) || defined(PSEUDO_DMA)
-		if (!cmd->device->borken &&
-		    !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
-		    (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) {
-
-		    len = transfersize;
-		    cmd->SCp.phase = phase;
-		    if (NCR5380_transfer_dma(instance, &phase,
-			&len, (unsigned char **) &cmd->SCp.ptr)) {
-			/*
-			 * If the watchdog timer fires, all future
-			 * accesses to this device will use the
-			 * polled-IO. */ 
-			printk(KERN_NOTICE "scsi%d: switching target %d "
-			       "lun %d to slow handshake\n", HOSTNO,
-			       cmd->target, cmd->lun);
-			cmd->device->borken = 1;
-			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-			    ICR_ASSERT_ATN);
-			sink = 1;
-			do_abort(instance);
-			cmd->result = DID_ERROR  << 16;
-			cmd->done(cmd);
-			/* XXX - need to source or sink data here, as appropriate */
-		    } else {
-#ifdef REAL_DMA
-			/* ++roman: When using real DMA,
-			 * information_transfer() should return after
-			 * starting DMA since it has nothing more to
-			 * do.
-			 */
-			return;
-#else			
-			/* Michael: When using pseudo-DMA emulation, we must 
-			 * take care to take into account the residual from 
-			 * the current transfer as determined by either the 
-			 * interrupt routine ot the pseudo-transfer functions
-			 * (whichever notices it first).
-			 */
-			if (mac_pdma_residual)
-			  len -= mac_pdma_residual;
-			cmd->SCp.this_residual -= transfersize - len;
-#endif
-		    }
-		} else
-#endif /* defined(REAL_DMA) || defined(PSEUDO_DMA) */
-		  NCR5380_transfer_pio(instance, &phase, 
-		    (int *) &cmd->SCp.this_residual, (unsigned char **)
-		    &cmd->SCp.ptr);
-		break;
-	    case PHASE_MSGIN:
-		len = 1;
-		data = &tmp;
-		NCR5380_write(SELECT_ENABLE_REG, 0); 	/* disable reselects */
-		NCR5380_transfer_pio(instance, &phase, &len, &data);
-		cmd->SCp.Message = tmp;
-
-		switch (tmp) {
-		/*
-		 * Linking lets us reduce the time required to get the 
-		 * next command out to the device, hopefully this will
-		 * mean we don't waste another revolution due to the delays
-		 * required by ARBITRATION and another SELECTION.
-		 *
-		 * In the current implementation proposal, low level drivers
-		 * merely have to start the next command, pointed to by 
-		 * next_link, done() is called as with unlinked commands.
-		 */
-#ifdef LINKED
-		case LINKED_CMD_COMPLETE:
-		case LINKED_FLG_CMD_COMPLETE:
-		    /* Accept message by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-		    
-		    LNK_PRINTK("scsi%d: target %d lun %d linked command "
-			       "complete.\n", HOSTNO, cmd->target, cmd->lun);
-
-		    /* Enable reselect interrupts */
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    /*
-		     * Sanity check : A linked command should only terminate
-		     * with one of these messages if there are more linked
-		     * commands available.
-		     */
-
-		    if (!cmd->next_link) {
-			 printk(KERN_NOTICE "scsi%d: target %d lun %d "
-				"linked command complete, no next_link\n",
-				HOSTNO, cmd->target, cmd->lun);
-			    sink = 1;
-			    do_abort (instance);
-			    return;
-		    }
-
-		    initialize_SCp(cmd->next_link);
-		    /* The next command is still part of this process; copy it
-		     * and don't free it! */
-		    cmd->next_link->tag = cmd->tag;
-		    cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
-		    LNK_PRINTK("scsi%d: target %d lun %d linked request "
-			       "done, calling scsi_done().\n",
-			       HOSTNO, cmd->target, cmd->lun);
-#ifdef NCR5380_STATS
-		    collect_stats(hostdata, cmd);
-#endif
-		    cmd->scsi_done(cmd);
-		    cmd = hostdata->connected;
-		    break;
-#endif /* def LINKED */
-		case ABORT:
-		case COMMAND_COMPLETE: 
-		    /* Accept message by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-		    hostdata->connected = NULL;
-		    QU_PRINTK("scsi%d: command for target %d, lun %d "
-			      "completed\n", HOSTNO, cmd->target, cmd->lun);
-#ifdef SUPPORT_TAGS
-		    cmd_free_tag( cmd );
-		    if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
-			/* Turn a QUEUE FULL status into BUSY, I think the
-			 * mid level cannot handle QUEUE FULL :-( (The
-			 * command is retried after BUSY). Also update our
-			 * queue size to the number of currently issued
-			 * commands now.
-			 */
-			/* ++Andreas: the mid level code knows about
-			   QUEUE_FULL now. */
-			TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
-			TAG_PRINTK("scsi%d: target %d lun %d returned "
-				   "QUEUE_FULL after %d commands\n",
-				   HOSTNO, cmd->target, cmd->lun,
-				   ta->nr_allocated);
-			if (ta->queue_size > ta->nr_allocated)
-			    ta->nr_allocated = ta->queue_size;
-		    }
-#else
-		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-#endif
-		    /* Enable reselect interrupts */
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-
-		    /* 
-		     * I'm not sure what the correct thing to do here is : 
-		     * 
-		     * If the command that just executed is NOT a request 
-		     * sense, the obvious thing to do is to set the result
-		     * code to the values of the stored parameters.
-		     * 
-		     * If it was a REQUEST SENSE command, we need some way to
-		     * differentiate between the failure code of the original
-		     * and the failure code of the REQUEST sense - the obvious
-		     * case is success, where we fall through and leave the
-		     * result code unchanged.
-		     * 
-		     * The non-obvious place is where the REQUEST SENSE failed
-		     */
-
-		    if (cmd->cmnd[0] != REQUEST_SENSE) 
-			cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
-		    else if (status_byte(cmd->SCp.Status) != GOOD)
-			cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-		    
-#ifdef AUTOSENSE
-		    if ((cmd->cmnd[0] != REQUEST_SENSE) && 
-			(status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
-			ASEN_PRINTK("scsi%d: performing request sense\n",
-				    HOSTNO);
-			cmd->cmnd[0] = REQUEST_SENSE;
-			cmd->cmnd[1] &= 0xe0;
-			cmd->cmnd[2] = 0;
-			cmd->cmnd[3] = 0;
-			cmd->cmnd[4] = sizeof(cmd->sense_buffer);
-			cmd->cmnd[5] = 0;
-			cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
-			cmd->use_sg = 0;
-			/* this is initialized from initialize_SCp 
-			cmd->SCp.buffer = NULL;
-			cmd->SCp.buffers_residual = 0;
-			*/
-			cmd->request_buffer = (char *) cmd->sense_buffer;
-			cmd->request_bufflen = sizeof(cmd->sense_buffer);
-
-			save_flags(flags);
-			cli();
-			LIST(cmd,hostdata->issue_queue);
-			NEXT(cmd) = hostdata->issue_queue;
-		        hostdata->issue_queue = (Scsi_Cmnd *) cmd;
-		        restore_flags(flags);
-			QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
-				  "issue queue\n", H_NO(cmd));
-		   } else
-#endif /* def AUTOSENSE */
-		   {
-#ifdef NCR5380_STATS
-		       collect_stats(hostdata, cmd);
-#endif
-		       cmd->scsi_done(cmd);
-		    }
-
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    /* 
-		     * Restore phase bits to 0 so an interrupted selection, 
-		     * arbitration can resume.
-		     */
-		    NCR5380_write(TARGET_COMMAND_REG, 0);
-		    
-		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
-			barrier();
-
-		    return;
-		case MESSAGE_REJECT:
-		    /* Accept message by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-		    /* Enable reselect interrupts */
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    switch (hostdata->last_message) {
-		    case HEAD_OF_QUEUE_TAG:
-		    case ORDERED_QUEUE_TAG:
-		    case SIMPLE_QUEUE_TAG:
-			/* The target obviously doesn't support tagged
-			 * queuing, even though it announced this ability in
-			 * its INQUIRY data ?!? (maybe only this LUN?) Ok,
-			 * clear 'tagged_supported' and lock the LUN, since
-			 * the command is treated as untagged further on.
-			 */
-			cmd->device->tagged_supported = 0;
-			hostdata->busy[cmd->target] |= (1 << cmd->lun);
-			cmd->tag = TAG_NONE;
-			TAG_PRINTK("scsi%d: target %d lun %d rejected "
-				   "QUEUE_TAG message; tagged queuing "
-				   "disabled\n",
-				   HOSTNO, cmd->target, cmd->lun);
-			break;
-		    }
-		    break;
-		case DISCONNECT:
-		    /* Accept message by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-		    save_flags(flags);
-		    cli();
-		    cmd->device->disconnect = 1;
-		    LIST(cmd,hostdata->disconnected_queue);
-		    NEXT(cmd) = hostdata->disconnected_queue;
-		    hostdata->connected = NULL;
-		    hostdata->disconnected_queue = cmd;
-		    restore_flags(flags);
-		    QU_PRINTK("scsi%d: command for target %d lun %d was "
-			      "moved from connected to the "
-			      "disconnected_queue\n", HOSTNO, 
-			      cmd->target, cmd->lun);
-		    /* 
-		     * Restore phase bits to 0 so an interrupted selection, 
-		     * arbitration can resume.
-		     */
-		    NCR5380_write(TARGET_COMMAND_REG, 0);
-
-		    /* Enable reselect interrupts */
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    /* Wait for bus free to avoid nasty timeouts */
-		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
-		    	barrier();
-		    return;
-		/* 
-		 * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
-		 * operation, in violation of the SCSI spec so we can safely 
-		 * ignore SAVE/RESTORE pointers calls.
-		 *
-		 * Unfortunately, some disks violate the SCSI spec and 
-		 * don't issue the required SAVE_POINTERS message before
-		 * disconnecting, and we have to break spec to remain 
-		 * compatible.
-		 */
-		case SAVE_POINTERS:
-		case RESTORE_POINTERS:
-		    /* Accept message by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-		    /* Enable reselect interrupts */
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    break;
-		case EXTENDED_MESSAGE:
-/* 
- * Extended messages are sent in the following format :
- * Byte 	
- * 0		EXTENDED_MESSAGE == 1
- * 1		length (includes one byte for code, doesn't 
- *		include first two bytes)
- * 2 		code
- * 3..length+1	arguments
- *
- * Start the extended message buffer with the EXTENDED_MESSAGE
- * byte, since print_msg() wants the whole thing.  
- */
-		    extended_msg[0] = EXTENDED_MESSAGE;
-		    /* Accept first byte by clearing ACK */
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-		    EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
-
-		    len = 2;
-		    data = extended_msg + 1;
-		    phase = PHASE_MSGIN;
-		    NCR5380_transfer_pio(instance, &phase, &len, &data);
-		    EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
-			       (int)extended_msg[1], (int)extended_msg[2]);
-
-		    if (!len && extended_msg[1] <= 
-			(sizeof (extended_msg) - 1)) {
-			/* Accept third byte by clearing ACK */
-			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-			len = extended_msg[1] - 1;
-			data = extended_msg + 3;
-			phase = PHASE_MSGIN;
-
-			NCR5380_transfer_pio(instance, &phase, &len, &data);
-			EXT_PRINTK("scsi%d: message received, residual %d\n",
-				   HOSTNO, len);
-
-			switch (extended_msg[2]) {
-			case EXTENDED_SDTR:
-			case EXTENDED_WDTR:
-			case EXTENDED_MODIFY_DATA_POINTER:
-			case EXTENDED_EXTENDED_IDENTIFY:
-			    tmp = 0;
-			}
-		    } else if (len) {
-			printk(KERN_NOTICE "scsi%d: error receiving "
-			       "extended message\n", HOSTNO);
-			tmp = 0;
-		    } else {
-			printk(KERN_NOTICE "scsi%d: extended message "
-			       "code %02x length %d is too long\n",
-			       HOSTNO, extended_msg[2], extended_msg[1]);
-			tmp = 0;
-		    }
-		/* Fall through to reject message */
-
-		/* 
-  		 * If we get something weird that we aren't expecting, 
- 		 * reject it.
-		 */
-		default:
-		    if (!tmp) {
-			printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
-			print_msg (extended_msg);
-			printk("\n");
-		    } else if (tmp != EXTENDED_MESSAGE)
-			printk(KERN_DEBUG "scsi%d: rejecting unknown "
-			       "message %02x from target %d, lun %d\n",
-			       HOSTNO, tmp, cmd->target, cmd->lun);
-		    else
-			printk(KERN_DEBUG "scsi%d: rejecting unknown "
-			       "extended message "
-			       "code %02x, length %d from target %d, lun %d\n",
-			       HOSTNO, extended_msg[1], extended_msg[0],
-			       cmd->target, cmd->lun);
-   
-
-		    msgout = MESSAGE_REJECT;
-		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-			ICR_ASSERT_ATN);
-		    break;
-		} /* switch (tmp) */
-		break;
-	    case PHASE_MSGOUT:
-		len = 1;
-		data = &msgout;
-		hostdata->last_message = msgout;
-		NCR5380_transfer_pio(instance, &phase, &len, &data);
-		if (msgout == ABORT) {
-#ifdef SUPPORT_TAGS
-		    cmd_free_tag( cmd );
-#else
-		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-#endif
-		    hostdata->connected = NULL;
-		    cmd->result = DID_ERROR << 16;
-#ifdef NCR5380_STATS
-		    collect_stats(hostdata, cmd);
-#endif
-		    cmd->scsi_done(cmd);
-		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-		    return;
-		}
-		msgout = NOP;
-		break;
-	    case PHASE_CMDOUT:
-		len = cmd->cmd_len;
-		data = cmd->cmnd;
-		/* 
-		 * XXX for performance reasons, on machines with a 
-		 * PSEUDO-DMA architecture we should probably 
-		 * use the dma transfer function.  
-		 */
-		NCR5380_transfer_pio(instance, &phase, &len, 
-		    &data);
-		break;
-	    case PHASE_STATIN:
-		len = 1;
-		data = &tmp;
-		NCR5380_transfer_pio(instance, &phase, &len, &data);
-		cmd->SCp.Status = tmp;
-		break;
-	    default:
-		printk("scsi%d: unknown phase\n", HOSTNO);
-		NCR_PRINT(NDEBUG_ANY);
-	    } /* switch(phase) */
-	} /* if (tmp * SR_REQ) */ 
-    } /* while (1) */
-}
-
-/*
- * Function : void NCR5380_reselect (struct Scsi_Host *instance)
- *
- * Purpose : does reselection, initializing the instance->connected 
- *	field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q 
- *	nexus has been reestablished,
- *	
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-
-static void NCR5380_reselect (struct Scsi_Host *instance)
-{
-    SETUP_HOSTDATA(instance);
-    unsigned char target_mask;
-    unsigned char lun, phase;
-    int len;
-#ifdef SUPPORT_TAGS
-    unsigned char tag;
-#endif
-    unsigned char msg[3];
-    unsigned char *data;
-    Scsi_Cmnd *tmp = NULL, *prev;
-/*    unsigned long flags; */
-
-    /*
-     * Disable arbitration, etc. since the host adapter obviously
-     * lost, and tell an interrupted NCR5380_select() to restart.
-     */
-
-    NCR5380_write(MODE_REG, MR_BASE);
-    hostdata->restart_select = 1;
-
-    target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
-
-    RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
-
-    /* 
-     * At this point, we have detected that our SCSI ID is on the bus,
-     * SEL is true and BSY was false for at least one bus settle delay
-     * (400 ns).
-     *
-     * We must assert BSY ourselves, until the target drops the SEL
-     * signal.
-     */
-
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
-    
-    while (NCR5380_read(STATUS_REG) & SR_SEL);
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-    /*
-     * Wait for target to go into MSGIN.
-     */
-
-    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
-
-    len = 1;
-    data = msg;
-    phase = PHASE_MSGIN;
-    NCR5380_transfer_pio(instance, &phase, &len, &data);
-
-    if (!msg[0] & 0x80) {
-	printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
-	print_msg(msg);
-	do_abort(instance);
-	return;
-    }
-    lun = (msg[0] & 0x07);
-
-#ifdef SUPPORT_TAGS
-    /* If the phase is still MSGIN, the target wants to send some more
-     * messages. In case it supports tagged queuing, this is probably a
-     * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
-     */
-    tag = TAG_NONE;
-    if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
-	/* Accept previous IDENTIFY message by clearing ACK */
-	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
-	len = 2;
-	data = msg+1;
-	if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
-	    msg[1] == SIMPLE_QUEUE_TAG)
-	    tag = msg[2];
-	TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
-		   "reselection\n", HOSTNO, target_mask, lun, tag);
-    }
-#endif
-    
-    /* 
-     * Find the command corresponding to the I_T_L or I_T_L_Q  nexus we 
-     * just reestablished, and remove it from the disconnected queue.
-     */
-
-    for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; 
-	 tmp; prev = tmp, tmp = NEXT(tmp) ) {
-	if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
-#ifdef SUPPORT_TAGS
-	    && (tag == tmp->tag) 
-#endif
-	    ) {
-	    if (prev) {
-		REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
-		NEXT(prev) = NEXT(tmp);
-	    } else {
-		REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
-		hostdata->disconnected_queue = NEXT(tmp);
-	    }
-	    NEXT(tmp) = NULL;
-	    break;
-	}
-    }
-    
-    if (!tmp) {
-	printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
-#ifdef SUPPORT_TAGS
-		"tag %d "
-#endif
-		"not in disconnect_queue.\n",
-		HOSTNO, target_mask, lun
-#ifdef SUPPORT_TAGS
-		, tag
-#endif
-		);
-	/* 
-	 * Since we have an established nexus that we can't do anything
-	 * with, we must abort it.  
-	 */
-	do_abort(instance);
-	return;
-    }
-
-    /* Accept message by clearing ACK */
-    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-    hostdata->connected = tmp;
-    RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
-	       HOSTNO, tmp->target, tmp->lun, tmp->tag);
-}
-
-
-/*
- * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
- *
- * Purpose : abort a command
- *
- * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the 
- * 	host byte of the result field to, if zero DID_ABORTED is 
- *	used.
- *
- * Returns : 0 - success, -1 on failure.
- *
- * XXX - there is no way to abort the command that is currently 
- * 	 connected, you have to wait for it to complete.  If this is 
- *	 a problem, we could implement longjmp() / setjmp(), setjmp()
- * 	 called where the loop started in NCR5380_main().
- */
-
-#ifndef NCR5380_abort
-static
-#endif
-int NCR5380_abort (Scsi_Cmnd *cmd)
-{
-    struct Scsi_Host *instance = cmd->host;
-    SETUP_HOSTDATA(instance);
-    Scsi_Cmnd *tmp, **prev;
-    unsigned long flags;
-
-    printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
-    print_Scsi_Cmnd (cmd);
-
-    NCR5380_print_status (instance);
-
-    save_flags(flags);
-    cli();
-    
-    ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
-		NCR5380_read(BUS_AND_STATUS_REG),
-		NCR5380_read(STATUS_REG));
-
-#if 1
-/* 
- * Case 1 : If the command is the currently executing command, 
- * we'll set the aborted flag and return control so that 
- * information transfer routine can exit cleanly.
- */
-
-    if (hostdata->connected == cmd) {
-
-	ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
-/*
- * We should perform BSY checking, and make sure we haven't slipped
- * into BUS FREE.
- */
-
-/*	NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
-/* 
- * Since we can't change phases until we've completed the current 
- * handshake, we have to source or sink a byte of data if the current
- * phase is not MSGOUT.
- */
-
-/* 
- * Return control to the executing NCR drive so we can clear the
- * aborted flag and get back into our main loop.
- */ 
-
-	if (do_abort(instance) == 0) {
-	  hostdata->aborted = 1;
-	  hostdata->connected = NULL;
-	  cmd->result = DID_ABORT << 16;
-#ifdef SUPPORT_TAGS
-	  cmd_free_tag( cmd );
-#else
-	  hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-#endif
-	  restore_flags(flags);
-	  cmd->scsi_done(cmd);
-	  return SCSI_ABORT_SUCCESS;
-	} else {
-/*	  restore_flags(flags); */
-	  printk("scsi%d: abort of connected command failed!\n", HOSTNO);
-	  return SCSI_ABORT_ERROR;
-	} 
-   }
-#endif
-
-/* 
- * Case 2 : If the command hasn't been issued yet, we simply remove it 
- * 	    from the issue queue.
- */
-    for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), 
-	tmp = (Scsi_Cmnd *) hostdata->issue_queue;
-	tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
-	if (cmd == tmp) {
-	    REMOVE(5, *prev, tmp, NEXT(tmp));
-	    (*prev) = NEXT(tmp);
-	    NEXT(tmp) = NULL;
-	    tmp->result = DID_ABORT << 16;
-	    restore_flags(flags);
-	    ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
-			HOSTNO);
-	    /* Tagged queuing note: no tag to free here, hasn't been assigned
-	     * yet... */
-	    tmp->scsi_done(tmp);
-	    return SCSI_ABORT_SUCCESS;
-	}
-
-/* 
- * Case 3 : If any commands are connected, we're going to fail the abort
- *	    and let the high level SCSI driver retry at a later time or 
- *	    issue a reset.
- *
- *	    Timeouts, and therefore aborted commands, will be highly unlikely
- *          and handling them cleanly in this situation would make the common
- *	    case of noresets less efficient, and would pollute our code.  So,
- *	    we fail.
- */
-
-    if (hostdata->connected) {
-	restore_flags(flags);
-	ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
-        return SCSI_ABORT_SNOOZE;
-    }
-
-/*
- * Case 4: If the command is currently disconnected from the bus, and 
- * 	there are no connected commands, we reconnect the I_T_L or 
- *	I_T_L_Q nexus associated with it, go into message out, and send 
- *      an abort message.
- *
- * This case is especially ugly. In order to reestablish the nexus, we
- * need to call NCR5380_select().  The easiest way to implement this 
- * function was to abort if the bus was busy, and let the interrupt
- * handler triggered on the SEL for reselect take care of lost arbitrations
- * where necessary, meaning interrupts need to be enabled.
- *
- * When interrupts are enabled, the queues may change - so we 
- * can't remove it from the disconnected queue before selecting it
- * because that could cause a failure in hashing the nexus if that 
- * device reselected.
- * 
- * Since the queues may change, we can't use the pointers from when we
- * first locate it.
- *
- * So, we must first locate the command, and if NCR5380_select()
- * succeeds, then issue the abort, relocate the command and remove
- * it from the disconnected queue.
- */
-
-    for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
-	 tmp = NEXT(tmp)) 
-        if (cmd == tmp) {
-            restore_flags(flags);
-	    ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
-  
-            if (NCR5380_select (instance, cmd, (int) cmd->tag)) 
-		return SCSI_ABORT_BUSY;
-
-	    ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
-
-	    do_abort (instance);
-
-	    save_flags(flags);
-	    cli();
-	    for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), 
-		tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
-		tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
-		    if (cmd == tmp) {
-		    REMOVE(5, *prev, tmp, NEXT(tmp));
-		    *prev = NEXT(tmp);
-		    NEXT(tmp) = NULL;
-		    tmp->result = DID_ABORT << 16;
-		    /* We must unlock the tag/LUN immediately here, since the
-		     * target goes to BUS FREE and doesn't send us another
-		     * message (COMMAND_COMPLETE or the like)
-		     */
-#ifdef SUPPORT_TAGS
-		    cmd_free_tag( tmp );
-#else
-		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-#endif
-		    restore_flags(flags);
-		    tmp->scsi_done(tmp);
-		    return SCSI_ABORT_SUCCESS;
-		}
-	}
-
-/*
- * Case 5 : If we reached this point, the command was not found in any of 
- *	    the queues.
- *
- * We probably reached this point because of an unlikely race condition
- * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case something really
- * broke.
- */
-
-    restore_flags(flags);
-    printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n"
-           KERN_INFO "        before abortion\n", HOSTNO); 
-
-/* Maybe it is sufficient just to release the ST-DMA lock... (if
- * possible at all) At least, we should check if the lock could be
- * released after the abort, in case it is kept due to some bug.
- */
-
-    return SCSI_ABORT_NOT_RUNNING;
-}
-
-
-/* 
- * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
- * 
- * Purpose : reset the SCSI bus.
- *
- * Returns : SCSI_RESET_WAKEUP
- *
- */ 
-
-static int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
-{
-    SETUP_HOSTDATA(cmd->host);
-    int           i;
-    unsigned long flags;
-#if 1
-    Scsi_Cmnd *connected, *disconnected_queue;
-#endif
-
-    NCR5380_print_status (cmd->host);
-
-    /* get in phase */
-    NCR5380_write( TARGET_COMMAND_REG,
-		   PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
-    /* assert RST */
-    NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
-    udelay (40);
-    /* reset NCR registers */
-    NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
-    NCR5380_write( MODE_REG, MR_BASE );
-    NCR5380_write( TARGET_COMMAND_REG, 0 );
-    NCR5380_write( SELECT_ENABLE_REG, 0 );
-    /* ++roman: reset interrupt condition! otherwise no interrupts don't get
-     * through anymore ... */
-    (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
-
-#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
-      /* XXX see below                                            XXX */
-
-    /* MSch: old-style reset: actually abort all command processing here */
-
-    /* After the reset, there are no more connected or disconnected commands
-     * and no busy units; to avoid problems with re-inserting the commands
-     * into the issue_queue (via scsi_done()), the aborted commands are
-     * remembered in local variables first.
-     */
-    save_flags(flags);
-    cli();
-    connected = (Scsi_Cmnd *)hostdata->connected;
-    hostdata->connected = NULL;
-    disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
-    hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
-    free_all_tags();
-#endif
-    for( i = 0; i < 8; ++i )
-	hostdata->busy[i] = 0;
-#ifdef REAL_DMA
-    hostdata->dma_len = 0;
-#endif
-    restore_flags(flags);
-
-    /* In order to tell the mid-level code which commands were aborted, 
-     * set the command status to DID_RESET and call scsi_done() !!!
-     * This ultimately aborts processing of these commands in the mid-level.
-     */
-
-    if ((cmd = connected)) {
-	ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
-	cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
-	cmd->scsi_done( cmd );
-    }
-
-    for (i = 0; (cmd = disconnected_queue); ++i) {
-	disconnected_queue = NEXT(cmd);
-	NEXT(cmd) = NULL;
-	cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
-	cmd->scsi_done( cmd );
-    }
-    if (i > 0)
-	ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
-
-    /* since all commands have been explicitly terminated, we need to tell
-     * the midlevel code that the reset was SUCCESSFUL, and there is no 
-     * need to 'wake up' the commands by a request_sense
-     */
-    return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
-#else /* 1 */
-
-    /* MSch: new-style reset handling: let the mid-level do what it can */
-
-    /* ++guenther: MID-LEVEL IS STILL BROKEN.
-     * Mid-level is supposed to requeue all commands that were active on the
-     * various low-level queues. In fact it does this, but that's not enough
-     * because all these commands are subject to timeout. And if a timeout
-     * happens for any removed command, *_abort() is called but all queues
-     * are now empty. Abort then gives up the falcon lock, which is fatal,
-     * since the mid-level will queue more commands and must have the lock
-     * (it's all happening inside timer interrupt handler!!).
-     * Even worse, abort will return NOT_RUNNING for all those commands not
-     * on any queue, so they won't be retried ...
-     *
-     * Conclusion: either scsi.c disables timeout for all resetted commands
-     * immediately, or we loose!  As of linux-2.0.20 it doesn't.
-     */
-
-    /* After the reset, there are no more connected or disconnected commands
-     * and no busy units; so clear the low-level status here to avoid 
-     * conflicts when the mid-level code tries to wake up the affected 
-     * commands!
-     */
-
-    if (hostdata->issue_queue)
-	ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
-    if (hostdata->connected) 
-	ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
-    if (hostdata->disconnected_queue)
-	ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
-
-    save_flags(flags);
-    cli();
-    hostdata->issue_queue = NULL;
-    hostdata->connected = NULL;
-    hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
-    free_all_tags();
-#endif
-    for( i = 0; i < 8; ++i )
-	hostdata->busy[i] = 0;
-#ifdef REAL_DMA
-    hostdata->dma_len = 0;
-#endif
-    restore_flags(flags);
-
-    /* we did no complete reset of all commands, so a wakeup is required */
-    return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
-#endif /* 1 */
-}
-
-/* Local Variables: */
-/* tab-width: 8     */
-/* End:             */
diff -urN linux-2.2.18-pre15/drivers/scsi/mac_esp.c linux/drivers/scsi/mac_esp.c
--- linux-2.2.18-pre15/drivers/scsi/mac_esp.c	Fri Dec 18 10:12:25 1998
+++ linux/drivers/scsi/mac_esp.c	Mon Dec  3 20:34:11 2001
@@ -28,14 +28,13 @@
 #include "NCR53C9x.h"
 #include "mac_esp.h"
 
-#include "../../arch/m68k/mac/via6522.h"  /* huh? */
-
 #include <asm/io.h>
 
 #include <asm/setup.h>
 #include <asm/irq.h>
 #include <asm/macints.h>
 #include <asm/machw.h>
+#include <asm/mac_via.h>
 
 #include <asm/pgtable.h>
 
@@ -59,10 +58,15 @@
 static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write);
 static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write);
 
-
 static int esp_dafb_dma_irq_p(struct NCR_ESP * espdev);
 static int esp_iosb_dma_irq_p(struct NCR_ESP * espdev);
 
+volatile unsigned char cmd_buffer[16];
+				/* This is where all commands are put
+				 * before they are transfered to the ESP chip
+				 * via PIO.
+				 */
+
 static int esp_initialized = 0;
 
 static int setup_num_esps = -1;
@@ -287,6 +291,9 @@
 	unsigned long timeout;
 #endif
 
+	if (esp_initialized > 0)
+		return -ENODEV;
+
 	/* what do we have in this machine... */
 	if (MACHW_PRESENT(MAC_SCSI_96)) {
 		chipspresent ++;
@@ -358,10 +365,13 @@
 
 		} /* chipnum == 0 */
 
-
 		/* use pio for command bytes; pio for message/data: TBI */
 		esp->do_pio_cmds = 1;
 
+		/* Set the command buffer */
+		esp->esp_command = (volatile unsigned char*) cmd_buffer;
+		esp->esp_command_dvma = (volatile unsigned char*) cmd_buffer;
+
 		/* various functions */
 		esp->dma_bytes_sent = &dma_bytes_sent;
 		esp->dma_can_transfer = &dma_can_transfer;
@@ -400,8 +410,7 @@
 
 			esp->irq = IRQ_MAC_SCSI;
 
-			request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp);
-			request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp);
+			request_irq(esp->irq, esp_intr, 0, "Mac ESP SCSI", esp);
 
 			if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
 				esp->cfreq = 16500000;
@@ -411,10 +420,8 @@
 
 
 		} else { /* chipnum == 1 */
-
-			esp->irq = IRQ_MAC_SCSIDRQ;
-
-			request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp);
+			esp->irq = IRQ_MAC_SCSI;
+			request_irq(esp->irq, esp_intr, 0, "Mac ESP SCSI 2", esp);
 
 			esp->cfreq = 25000000;
 
@@ -469,7 +476,7 @@
 static int esp_dafb_dma_irq_p(struct NCR_ESP * esp)
 {
 	unsigned int ret;
-	int sreg = esp->eregs->esp_status;
+	int sreg = esp_read(esp->eregs->esp_status);
 
 #ifdef DEBUG_MAC_ESP
 	printk("mac_esp: esp_dafb_dma_irq_p dafb %d irq %d\n", 
@@ -510,7 +517,7 @@
 static int esp_iosb_dma_irq_p(struct NCR_ESP * esp)
 {
 	int ret  = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
-	int sreg = esp->eregs->esp_status;
+	int sreg = esp_read(esp->eregs->esp_status);
 
 #ifdef DEBUG_MAC_ESP
 	printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n", 
@@ -599,13 +606,13 @@
 
 static void dma_ints_off(struct NCR_ESP * esp)
 {
-	mac_turnoff_irq(esp->irq);
+	mac_disable_irq(esp->irq);
 }
 
 
 static void dma_ints_on(struct NCR_ESP * esp)
 {
-	mac_turnon_irq(esp->irq);
+	mac_enable_irq(esp->irq);
 }
 
 /*
@@ -614,7 +621,7 @@
 
 static int dma_irq_p(struct NCR_ESP * esp)
 {
-	int i = esp->eregs->esp_status;
+	int i = esp_read(esp->eregs->esp_status);
 
 #ifdef DEBUG_MAC_ESP
 	printk("mac_esp: dma_irq_p status %d\n", i);
@@ -629,7 +636,7 @@
 	 * Copied from iosb_dma_irq_p()
 	 */
 	int ret  = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
-	int sreg = esp->eregs->esp_status;
+	int sreg = esp_read(esp->eregs->esp_status);
 
 #ifdef DEBUG_MAC_ESP
 	printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n", 
diff -urN linux-2.2.18-pre15/drivers/scsi/mac_esp.h linux/drivers/scsi/mac_esp.h
--- linux-2.2.18-pre15/drivers/scsi/mac_esp.h	Tue May 11 10:36:56 1999
+++ linux/drivers/scsi/mac_esp.h	Mon Dec  3 20:34:11 2001
@@ -19,9 +19,11 @@
 extern int esp_command(Scsi_Cmnd *);
 extern int esp_abort(Scsi_Cmnd *);
 extern int esp_reset(Scsi_Cmnd *, unsigned int);
-
+extern int esp_proc_info(char *buffer, char **start, off_t offset, int length,
+			int hostno, int inout);
 
 #define SCSI_MAC_ESP      { proc_dir:		&proc_scsi_esp, \
+			    proc_info:		esp_proc_info, \
 			    name:		"Mac 53C9x SCSI", \
 			    detect:		mac_esp_detect, \
 			    release:		NULL, \
diff -urN linux-2.2.18-pre15/drivers/scsi/mac_scsi.c linux/drivers/scsi/mac_scsi.c
--- linux-2.2.18-pre15/drivers/scsi/mac_scsi.c	Fri Jan 15 14:41:04 1999
+++ linux/drivers/scsi/mac_scsi.c	Mon Dec  3 20:34:11 2001
@@ -1,553 +1,115 @@
 /*
- * mac_scsi.c   -- Device dependent functions for the Macintosh NCR5380 SCSI 
- *		   port (MacII style machines, no DMA).
+ * Generic Macintosh NCR5380 driver
  *
- * based on:
+ * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
+ *
+ * Pseudo-DMA, Ove Edlund <ove.edlund@sm.luth.se>, June 2000
+ *
+ * derived in part from:
  */
-
 /*
- * atari_scsi.c -- Device dependent functions for the Atari generic SCSI port
+ * Generic Generic NCR5380 driver
  *
- * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Copyright 1995, Russell King
  *
- *   Loosely based on the work of Robert De Vries' team and added:
- *    - working real DMA
- *    - Falcon support (untested yet!)   ++bjoern fixed and now it works
- *    - lots of extensions and bug fixes.
+ * ALPHA RELEASE 1.
  *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive
- * for more details.
+ * For more information, please consult
  *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
  */
 
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#define NDEBUG_ABORT	0x800000
-#define NDEBUG_TAGS	0x1000000
-#define NDEBUG_MERGING	0x2000000
-
-#define NDEBUG (0)
-
-#define AUTOSENSE
-/* MSch: Tested the pseudo-DMA code on Atari for the Mac68k port ... */
-/* Defining neither PSEUDO_DMA nor REAL_DMA -> PIO transfer, sloooow ! */
-/*#define	REAL_DMA*/ /* never supported on NCR5380 Macs */
 /*
- * Usage: define PSEUDO_DMA to use hardware-handshaked PIO mode (TBI)
- *        undef  PSEUDO_DMA to use pure PIO mode
+ * $Log: mac_NCR5380.c,v $
  */
 
-/*#define PSEUDO_DMA*/	/* currently gives trouble on some Macs */
-
-#ifdef PSEUDO_DMA
-#define EMULATE_PSEUDO_DMA
-#define DMA_WORKS_RIGHT
-#define UNSAFE
-#endif
-
-/* Support tagged queuing? (on devices that are able to... :-) */
-#define	SUPPORT_TAGS
-#define	MAX_TAGS 32
-
 #include <linux/types.h>
 #include <linux/stddef.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/blk.h>
+
+#include <linux/module.h>
+#include <linux/signal.h>
 #include <linux/sched.h>
-#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blk.h>
 
-#include <asm/setup.h>
-#include <asm/machw.h>
-#include <asm/macints.h>
-#include <asm/macintosh.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
+#include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/traps.h>
-#include <asm/bitops.h>
+#include <asm/system.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
 
 #include "scsi.h"
 #include "hosts.h"
 #include "mac_scsi.h"
 #include "NCR5380.h"
 #include "constants.h"
-#include <asm/io.h>
-
-#include <linux/stat.h>
-
-struct proc_dir_entry proc_scsi_mac = {
-    PROC_SCSI_MAC, 8, "mac_5380",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
 
-/*
- * Define RBV_HACK to run the SCSI driver on RBV Macs; undefine to
- * try getting better interrupt latency
- */
-#define RBV_HACK
-
-#ifdef RBV_HACK
-#define	ENABLE_IRQ()	mac_turnon_irq( IRQ_MAC_SCSI ); 
-#define	DISABLE_IRQ()	mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
-#define	ENABLE_IRQ()	mac_enable_irq( IRQ_MAC_SCSI ); 
-#define	DISABLE_IRQ()	mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
-#define HOSTDATA_DMALEN		(((struct NCR5380_hostdata *) \
-				(mac_scsi_host->hostdata))->dma_len)
-
-/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
- * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
- * need ten times the standard value... */
-#ifndef CONFIG_MAC_SCSI_TOSHIBA_DELAY
-#define	AFTER_RESET_DELAY	(HZ/2)
+#if 0
+#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION)
 #else
-#define	AFTER_RESET_DELAY	(5*HZ/2)
+#define NDEBUG (NDEBUG_ABORT)
 #endif
 
-/***************************** Prototypes *****************************/
-
-#ifdef REAL_DMA
-static int scsi_dma_is_ignored_buserr( unsigned char dma_stat );
-static void mac_scsi_fetch_restbytes( void );
-static long mac_scsi_dma_residual( struct Scsi_Host *instance );
-static int mac_classify_cmd( Scsi_Cmnd *cmd );
-static unsigned long mac_dma_xfer_len( unsigned long wanted_len,
-                                       Scsi_Cmnd *cmd, int write_flag );
-#endif
-#ifdef PSEUDO_DMA
-static int mac_pdma_read(struct Scsi_Host *instance, unsigned char *dst,
-			   int len);
-static int mac_pdma_write(struct Scsi_Host *instance, unsigned char *src,
-			   int len);
-static unsigned long mac_dma_xfer_len( unsigned long wanted_len,
-                                         Scsi_Cmnd *cmd, int write_flag );
-#endif
-static void scsi_mac_intr( int irq, void *dummy, struct pt_regs *fp);
-static void mac_scsi_reset_boot( void );
-static unsigned char mac_scsi_reg_read( unsigned char reg );
-static void mac_scsi_reg_write( unsigned char reg, unsigned char value);
-
-/************************* End of Prototypes **************************/
+#define	ENABLE_IRQ()	mac_enable_irq( IRQ_MAC_SCSI ); 
+#define	DISABLE_IRQ()	mac_disable_irq( IRQ_MAC_SCSI );
 
+extern void via_scsi_clear(void);
 
-static struct Scsi_Host *mac_scsi_host = NULL;
-#if 0
-static unsigned char (*mac_scsi_reg_read)( unsigned char reg );
-static void (*mac_scsi_reg_write)( unsigned char reg, unsigned char value );
-#endif
+static void mac_scsi_reset_boot(struct Scsi_Host *instance);
+static char macscsi_read(struct Scsi_Host *instance, int reg);
+static void macscsi_write(struct Scsi_Host *instance, int reg, int value);
 
-#ifdef REAL_DMA
-static unsigned long	mac_dma_residual, mac_dma_startaddr;
-static short		mac_dma_active;
-/* pointer to the dribble buffer */
-static char		*mac_dma_buffer = NULL;
-/* precalculated physical address of the dribble buffer */
-static unsigned long	mac_dma_phys_buffer;
-/* != 0 tells the int handler to copy data from the dribble buffer */
-static char		*mac_dma_orig_addr;
-/* size of the dribble buffer; 4k seems enough, since the Falcon cannot use
- * scatter-gather anyway, so most transfers are 1024 byte only. In the rare
- * cases where requests to physical contiguous buffers have been merged, this
- * request is <= 4k (one page). So I don't think we have to split transfers
- * just due to this buffer size...
- */
-#define	MAC_BUFFER_SIZE	(4096)
-#if 1  /* FIXME: is that an issue for Macs?? */
-/* mask for address bits that can't be used with the ST-DMA */
-static unsigned long	mac_dma_stram_mask;
-#define STRAM_ADDR(a)	(((a) & mac_dma_stram_mask) == 0)
-#endif
-/* number of bytes to cut from a transfer to handle NCR overruns */
-static int mac_read_overruns = 0;
-#endif
-#ifdef PSEUDO_DMA
-static unsigned long	mac_pdma_residual, mac_pdma_startaddr, mac_pdma_current;
-static short		mac_pdma_active;
-/* FIXME: is that an issue for Macs?? */
-/* mask for address bits that can't be used with the ST-DMA */
-static unsigned long	mac_dma_stram_mask;
-#define STRAM_ADDR(a)	(((a) & mac_dma_stram_mask) == 0)
-static int mac_read_overruns = 0;
-#endif
 static int setup_can_queue = -1;
 static int setup_cmd_per_lun = -1;
 static int setup_sg_tablesize = -1;
-#ifdef SUPPORT_TAGS
 static int setup_use_tagged_queuing = -1;
-#endif
 static int setup_hostid = -1;
 
+/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
+ * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
+ * need ten times the standard value... */
+#define TOSHIBA_DELAY
 
-#if defined(REAL_DMA)
-
-#if 0  /* FIXME */
-static int scsi_dma_is_ignored_buserr( unsigned char dma_stat )
-{
-	int i;
-	unsigned long	addr = SCSI_DMA_READ_P( dma_addr ), end_addr;
-
-	if (dma_stat & 0x01) {
-
-		/* A bus error happens when DMA-ing from the last page of a
-		 * physical memory chunk (DMA prefetch!), but that doesn't hurt.
-		 * Check for this case:
-		 */
-		
-		for( i = 0; i < boot_info.num_memory; ++i ) {
-			end_addr = boot_info.memory[i].addr +
-				boot_info.memory[i].size;
-			if (end_addr <= addr && addr <= end_addr + 4)
-				return( 1 );
-		}
-	}
-	return( 0 );
-}
-
-
-/* Dead code... wasn't called anyway :-) and causes some trouble, because at
- * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has
- * to clear the DMA int pending bit before it allows other level 6 interrupts.
- */
-static void scsi_dma_buserr (int irq, void *dummy, struct pt_regs *fp)
-{
-	unsigned char	dma_stat = tt_scsi_dma.dma_ctrl;
-
-	/* Don't do anything if a NCR interrupt is pending. Probably it's just
-	 * masked... */
-	if (mac_irq_pending( IRQ_MAC_SCSI ))
-		return;
-	
-	printk("Bad SCSI DMA interrupt! dma_addr=0x%08lx dma_stat=%02x dma_cnt=%08lx\n",
-	       SCSI_DMA_READ_P(dma_addr), dma_stat, SCSI_DMA_READ_P(dma_cnt));
-	if (dma_stat & 0x80) {
-		if (!scsi_dma_is_ignored_buserr( dma_stat ))
-			printk( "SCSI DMA bus error -- bad DMA programming!\n" );
-	}
-	else {
-		/* Under normal circumstances we never should get to this point,
-		 * since both interrupts are triggered simultaneously and the 5380
-		 * int has higher priority. When this irq is handled, that DMA
-		 * interrupt is cleared. So a warning message is printed here.
-		 */
-		printk( "SCSI DMA intr ?? -- this shouldn't happen!\n" );
-	}
-}
-#endif
-
-#endif
-
-void restore_irq(struct pt_regs *regs)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	flags = (flags & ~0x0700) | (regs->sr & 0x0700);
-	restore_flags(flags);
-}
-
-static int polled_scsi_on = 0;
-static unsigned char *mac_scsi_regp = NULL;
-
-void scsi_mac_polled (void)
-{
-	if (polled_scsi_on)
-	{
-		if(NCR5380_read(BUS_AND_STATUS_REG)&BASR_IRQ)
-		{
-			printk("SCSI poll\n");
-			scsi_mac_intr(IRQ_MAC_SCSI, NULL, NULL);
-		}
-	}
-}
-
-static void scsi_mac_intr (int irq, void *dummy, struct pt_regs *fp)
-{
-	unsigned long flags;
-#ifdef REAL_DMA
-	int dma_stat;
-
-	dma_stat = mac_scsi_dma.dma_ctrl;
-
-	INT_PRINTK("scsi%d: NCR5380 interrupt, DMA status = %02x\n",
-		   mac_scsi_host->host_no, dma_stat & 0xff);
-
-	/* Look if it was the DMA that has interrupted: First possibility
-	 * is that a bus error occurred...
-	 */
-	if (dma_stat & 0x80) {
-		if (!scsi_dma_is_ignored_buserr( dma_stat )) {
-			printk(KERN_ERR "SCSI DMA caused bus error near 0x%08lx\n",
-			       SCSI_DMA_READ_P(dma_addr));
-			printk(KERN_CRIT "SCSI DMA bus error -- bad DMA programming!");
-		}
-	}
-
-	/* If the DMA is active but not finished, we have the the case
-	 * that some other 5380 interrupt occurred within the DMA transfer.
-	 * This means we have residual bytes, if the desired end address
-	 * is not yet reached. Maybe we have to fetch some bytes from the
-	 * rest data register, too. The residual must be calculated from
-	 * the address pointer, not the counter register, because only the
-	 * addr reg counts bytes not yet written and pending in the rest
-	 * data reg!
-	 */
-	if ((dma_stat & 0x02) && !(dma_stat & 0x40)) {
-		mac_dma_residual = HOSTDATA_DMALEN - (SCSI_DMA_READ_P( dma_addr ) -
-												mac_dma_startaddr);
-
-		DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n",
-			   mac_dma_residual);
-
-		if ((signed int)mac_dma_residual < 0)
-			mac_dma_residual = 0;
-		if ((dma_stat & 1) == 0) {
-			/* After read operations, we maybe have to
-			   transport some rest bytes */
-			mac_scsi_fetch_restbytes();
-		}
-		else {
-			/* There seems to be a nasty bug in some SCSI-DMA/NCR
-			   combinations: If a target disconnects while a write
-			   operation is going on, the address register of the
-			   DMA may be a few bytes farer than it actually read.
-			   This is probably due to DMA prefetching and a delay
-			   between DMA and NCR.  Experiments showed that the
-			   dma_addr is 9 bytes to high, but this could vary.
-			   The problem is, that the residual is thus calculated
-			   wrong and the next transfer will start behind where
-			   it should.  So we round up the residual to the next
-			   multiple of a sector size, if it isn't already a
-			   multiple and the originally expected transfer size
-			   was.  The latter condition is there to ensure that
-			   the correction is taken only for "real" data
-			   transfers and not for, e.g., the parameters of some
-			   other command.  These shouldn't disconnect anyway.
-			   */
-			if (mac_dma_residual & 0x1ff) {
-				DMA_PRINTK("SCSI DMA: DMA bug corrected, "
-					   "difference %ld bytes\n",
-					   512 - (mac_dma_residual & 0x1ff));
-				mac_dma_residual = (mac_dma_residual + 511) & ~0x1ff;
-			}
-		}
-		mac_scsi_dma.dma_ctrl = 0;
-	}
-
-	/* If the DMA is finished, fetch the rest bytes and turn it off */
-	if (dma_stat & 0x40) {
-		atari_dma_residual = 0;
-		if ((dma_stat & 1) == 0)
-			atari_scsi_fetch_restbytes();
-		tt_scsi_dma.dma_ctrl = 0;
-	}
-
-#endif /* REAL_DMA */
-
-#ifdef PSEUDO_DMA
-	/* determine if there is any residual for the current transfer */
-	if (mac_pdma_active) {
-	  /* probably EOP interrupt, signaling i.e. target disconnect.
-	   * We must figure out the residual from the source/destination
-	   * pointers here ... */
-	  /* Should check bus status here to make sure it wasn't reselect or reset */
-	  mac_pdma_residual = HOSTDATA_DMALEN - (mac_pdma_current - mac_pdma_startaddr);
-	  mac_pdma_active = 0;
-	}
-#endif
-
-#ifdef RBV_HACK
-	mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
-	mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
-	save_flags(flags);
-#ifndef RBV_HACK	/* interferes with level triggered RBV IRQs ?? */
-	restore_irq(fp);
-#endif
-
-	if ( irq == IRQ_IDX(IRQ_MAC_SCSI) )
-		NCR5380_intr (irq, dummy, fp);
-
-	restore_flags(flags);
-
-	/* To be sure the int is not masked */
-#ifdef RBV_HACK
-	mac_turnon_irq( IRQ_MAC_SCSI );
-#else
-	mac_enable_irq( IRQ_MAC_SCSI );
-#endif
-
-	/* Clear the IRQ */
-	via_scsi_clear();
-}
-
-
-#ifdef REAL_DMA
-static void mac_scsi_fetch_restbytes( void )
-{
-	int nr;
-	char	*src, *dst;
-
-	/* fetch rest bytes in the DMA register */
-	dst = (char *)SCSI_DMA_READ_P( dma_addr );
-	if ((nr = ((long)dst & 3))) {
-		/* there are 'nr' bytes left for the last long address before the
-		   DMA pointer */
-		dst = (char *)( (unsigned long)dst & ~3 );
-		DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx",
-			   nr, (long)dst);
-		dst = (char *)PTOV(dst);  /* The content of the DMA pointer
-					   * is a physical address! */
-		DMA_PRINTK(" = virt addr 0x%08lx\n", (long)dst);
-		for( src = (char *)&mac_scsi_dma.dma_restdata; nr > 0; --nr )
-			*dst++ = *src++;
-	}
-}
-#endif /* REAL_DMA */
-
-#if 0  /* FIXME : how is the host ID determined on a Mac? */
-#define	RTC_READ(reg)				\
-    ({	unsigned char	__val;			\
-		outb(reg,&tt_rtc.regsel);	\
-		__val = tt_rtc.data;		\
-		__val;				\
-	})
-
-#define	RTC_WRITE(reg,val)			\
-    do {					\
-		outb(reg,&tt_rtc.regsel);	\
-		tt_rtc.data = (val);		\
-	} while(0)
-#endif
-				   
-int mac_scsi_detect (Scsi_Host_Template *host)
-{
-	static int called = 0;
-	struct Scsi_Host *instance;
-
-	if (!MACH_IS_MAC || called)
-		return( 0 );
-
-	if (macintosh_config->scsi_type != MAC_SCSI_OLD)
-		return( 0 );
-
-	host->proc_dir = &proc_scsi_mac;
-
-	/* testing: IIfx SCSI without IOP ?? */
-	if (macintosh_config->ident == MAC_MODEL_IIFX)
-		mac_scsi_regp = via1_regp+0x8000;
-	else
-		mac_scsi_regp = via1_regp+0x10000;
-
-#if 0  /* maybe if different SCSI versions show up ? */
-	mac_scsi_reg_read  = IS_A_TT() ? atari_scsi_tt_reg_read :
-					   atari_scsi_falcon_reg_read;
-	mac_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write :
-#endif					   atari_scsi_falcon_reg_write;
-
-	/* setup variables */
-	host->can_queue =
-		(setup_can_queue > 0) ? setup_can_queue :
-		MAC_SCSI_CAN_QUEUE;
-	host->cmd_per_lun =
-		(setup_cmd_per_lun > 0) ? setup_cmd_per_lun :
-		MAC_SCSI_CMD_PER_LUN;
-	host->sg_tablesize =
-		(setup_sg_tablesize >= 0) ? setup_sg_tablesize : MAC_SCSI_SG_TABLESIZE;
-
-	if (setup_hostid >= 0)
-		host->this_id = setup_hostid;
-	else {
-		/* use 7 as default */
-		host->this_id = 7;
-	}
-
-#ifdef SUPPORT_TAGS
-	if (setup_use_tagged_queuing < 0)
-		setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
-#endif
-
-	instance = scsi_register (host, sizeof (struct NCR5380_hostdata));
-	mac_scsi_host = instance;
-
-	/* truncation of machspec bit not critical as instance->irq never used */
-#if 0	/* Might work; problem was only with Falcon lock */
-	instance->irq = IRQ_MAC_SCSI;
+#ifdef TOSHIBA_DELAY
+#define	AFTER_RESET_DELAY	(5*HZ/2)
 #else
-	instance->irq = 0;
-#endif
-	mac_scsi_reset_boot();
-	NCR5380_init (instance, 0);
-
-       	/* This int is actually "pseudo-slow", i.e. it acts like a slow
-	 * interrupt after having cleared the pending flag for the DMA
-	 * interrupt. */
-	request_irq(IRQ_MAC_SCSI, scsi_mac_intr, IRQ_TYPE_SLOW,
-	            "SCSI NCR5380", scsi_mac_intr);
-#ifdef REAL_DMA
-	tt_scsi_dma.dma_ctrl = 0;
-	atari_dma_residual = 0;
-
-	if (is_brokenscsi) {
-		/* While the read overruns (described by Drew Eckhardt in
-		 * NCR5380.c) never happened on TTs, they do in fact on the Medusa
-		 * (This was the cause why SCSI didn't work right for so long
-		 * there.) Since handling the overruns slows down a bit, I turned
-		 * the #ifdef's into a runtime condition.
-		 *
-		 * In principle it should be sufficient to do max. 1 byte with
-		 * PIO, but there is another problem on the Medusa with the DMA
-		 * rest data register. So 'atari_read_overruns' is currently set
-		 * to 4 to avoid having transfers that aren't a multiple of 4. If
-		 * the rest data bug is fixed, this can be lowered to 1.
-		 */
-		mac_read_overruns = 4;
-	}
-#endif /* REAL_DMA */
-
-#ifdef PSEUDO_DMA
-	mac_pdma_residual = 0;
-	mac_pdma_active   = 0;
+#define	AFTER_RESET_DELAY	(HZ/2)
 #endif
 
-	printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d "
-#ifdef SUPPORT_TAGS
-			"TAGGED-QUEUING=%s "
-#endif
-			"HOSTID=%d",
-			instance->host_no, instance->hostt->can_queue,
-			instance->hostt->cmd_per_lun,
-			instance->hostt->sg_tablesize,
-#ifdef SUPPORT_TAGS
-			setup_use_tagged_queuing ? "yes" : "no",
-#endif
-			instance->hostt->this_id );
-	NCR5380_print_options (instance);
-	printk ("\n");
+static struct proc_dir_entry proc_scsi_mac5380 = {
+	PROC_SCSI_MAC, 13, "Mac 5380 SCSI", S_IFDIR | S_IRUGO, S_IXUGO, 2
+};
 
-	called = 1;
-	return( 1 );
-}
+/* Must move these into a per-host thingy once the DuoDock is supported */
+static volatile unsigned char *mac_scsi_regp = NULL;
+static volatile unsigned char *mac_scsi_drq  = NULL;
+static volatile unsigned char *mac_scsi_nodrq = NULL;
 
-#ifdef MODULE
-int mac_scsi_release (struct Scsi_Host *sh)
-{
-       	free_irq(IRQ_MAC_SCSI, scsi_mac_intr);
-	if (mac_dma_buffer)
-		scsi_init_free (mac_dma_buffer, MAC_BUFFER_SIZE);
-	return 1;
-}
-#endif
+/*
+ * Function : mac_scsi_setup(char *str, int *ints)
+ *
+ * Purpose : booter command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ */
 
-void mac_scsi_setup( char *str, int *ints )
+void mac_scsi_setup(char *str, int *ints)
 {
 	/* Format of mac5380 parameter is:
 	 *   mac5380=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
@@ -600,45 +162,138 @@
 		else if (ints[4] > 7)
 			printk( "mac_scsi_setup: invalid host ID %d !\n", ints[4] );
 	}
-#ifdef SUPPORT_TAGS
 	if (ints[0] >= 5) {
 		if (ints[5] >= 0)
 			setup_use_tagged_queuing = !!ints[5];
 	}
-#endif
 }
 
-int mac_scsi_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
+/*
+ * XXX: status debug
+ */
+static struct Scsi_Host *default_instance;
+
+/*
+ * Function : int macscsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes mac NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+ 
+int macscsi_detect(Scsi_Host_Template * tpnt)
 {
-	int		rv;
-	struct NCR5380_hostdata *hostdata =
-		(struct NCR5380_hostdata *)cmd->host->hostdata;
-
-	/* For doing the reset, SCSI interrupts must be disabled first,
-	 * since the 5380 raises its IRQ line while _RST is active and we
-	 * can't disable interrupts completely, since we need the timer.
-	 */
-	/* And abort a maybe active DMA transfer */
-       	mac_turnoff_irq( IRQ_MAC_SCSI );
-#ifdef REAL_DMA
-       	mac_scsi_dma.dma_ctrl = 0;
-#endif /* REAL_DMA */
-#ifdef PSEUDO_DMA
-	mac_pdma_active = 0;
-#endif
+    static int called = 0;
+    int flags = 0;
+    struct Scsi_Host *instance;
+
+    if (!MACH_IS_MAC || called)
+	return( 0 );
+
+    if (macintosh_config->scsi_type != MAC_SCSI_OLD)
+	return( 0 );
+
+    tpnt->proc_dir = &proc_scsi_mac5380;
+
+    /* setup variables */
+    tpnt->can_queue =
+	(setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
+    tpnt->cmd_per_lun =
+	(setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
+    tpnt->sg_tablesize = 
+	(setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
+
+    if (setup_hostid >= 0)
+	tpnt->this_id = setup_hostid;
+    else {
+	/* use 7 as default */
+	tpnt->this_id = 7;
+    }
+
+    if (setup_use_tagged_queuing < 0)
+	setup_use_tagged_queuing = USE_TAGGED_QUEUING;
 
-	rv = NCR5380_reset(cmd, reset_flags);
+    /* Once we support multiple 5380s (e.g. DuoDock) we'll do
+       something different here */
+    instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+    default_instance = instance;
+    
+    if (macintosh_config->ident == MAC_MODEL_IIFX) {
+	mac_scsi_regp  = via1+0x8000;
+	mac_scsi_drq   = via1+0xE000;
+	mac_scsi_nodrq = via1+0xC000;
+	flags = FLAG_NO_PSEUDO_DMA;
+    } else {
+	mac_scsi_regp  = via1+0x10000;
+	mac_scsi_drq   = via1+0x6000;
+	mac_scsi_nodrq = via1+0x12000;
+    }
+
+    instance->io_port = (unsigned long) mac_scsi_regp;
+    instance->irq = IRQ_MAC_SCSI;
+
+    /* Turn off pseudo DMA and IRQs for II, IIx, IIcx, and SE/30.
+     * XXX: This is a kludge until we can figure out why interrupts 
+     * won't work.
+     */
+   
+    if (macintosh_config->via_type == MAC_VIA_II) {
+        flags = FLAG_NO_PSEUDO_DMA;
+        instance->irq = IRQ_NONE;
+    }
+   
+    mac_scsi_reset_boot(instance);
 
-	/* Re-enable ints */
-       	mac_turnon_irq( IRQ_MAC_SCSI );
+    NCR5380_init(instance, flags);
 
-	return( rv );
+    instance->n_io_port = 255;
+
+    ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+
+    if (instance->irq != IRQ_NONE)
+	if (request_irq(instance->irq, macscsi_intr, 0, "MacSCSI-5380",
+			(void *) mac_scsi_regp)) {
+	    printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		   instance->host_no, instance->irq);
+	    instance->irq = IRQ_NONE;
+	}
+
+    printk("scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
+    if (instance->irq == IRQ_NONE)
+	printk ("s disabled");
+    else
+	printk (" %d", instance->irq);
+    printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+	   instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE);
+    printk("\nscsi%d:", instance->host_no);
+    NCR5380_print_options(instance);
+    printk("\n");
+    called = 1;
+    return 1;
 }
 
-	
-static void mac_scsi_reset_boot( void )
+int macscsi_release (struct Scsi_Host *shpnt)
+{
+	if (shpnt->irq != IRQ_NONE)
+		free_irq (shpnt->irq, NULL);
+
+	return 0;
+}
+
+/*
+ * Our 'bus reset on boot' function
+ */
+
+static void mac_scsi_reset_boot(struct Scsi_Host *instance)
 {
 	unsigned long end;
+
+	NCR5380_local_declare();
+	NCR5380_setup(instance);
 	
 	/*
 	 * Do a SCSI reset to clean up the bus during initialization. No messing
@@ -648,7 +303,7 @@
 	printk( "Macintosh SCSI: resetting the SCSI bus..." );
 
 	/* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
-       	mac_turnoff_irq( IRQ_MAC_SCSI );
+	DISABLE_IRQ()
 
 	/* get in phase */
 	NCR5380_write( TARGET_COMMAND_REG,
@@ -662,446 +317,186 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
+	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
 		barrier();
 
 	/* switch on SCSI IRQ again */
-       	mac_turnon_irq( IRQ_MAC_SCSI );
+	ENABLE_IRQ()
 
 	printk( " done\n" );
 }
 
-
-const char * mac_scsi_info (struct Scsi_Host *host)
-{
-	/* mac_scsi_detect() is verbose enough... */
-	static const char string[] = "Macintosh NCR5380 SCSI";
-	return string;
-}
-
-
-#if defined(REAL_DMA)
-
-unsigned long mac_scsi_dma_setup( struct Scsi_Host *instance, void *data,
-				   unsigned long count, int dir )
-{
-	unsigned long addr = VTOP( data );
-
-	DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, "
-		   "dir = %d\n", instance->host_no, data, addr, count, dir);
-
-	if (!STRAM_ADDR(addr)) {
-		/* If we have a non-DMAable address on a Falcon, use the dribble
-		 * buffer; 'orig_addr' != 0 in the read case tells the interrupt
-		 * handler to copy data from the dribble buffer to the originally
-		 * wanted address.
-		 */
-		if (dir)
-			memcpy( mac_dma_buffer, data, count );
-		else
-			mac_dma_orig_addr = data;
-		addr = mac_dma_phys_buffer;
-	}
-	
-	mac_dma_startaddr = addr;	/* Needed for calculating residual later. */
-  
-	/* Cache cleanup stuff: On writes, push any dirty cache out before sending
-	 * it to the peripheral. (Must be done before DMA setup, since at least
-	 * the ST-DMA begins to fill internal buffers right after setup. For
-	 * reads, invalidate any cache, may be altered after DMA without CPU
-	 * knowledge.
-	 * 
-	 * ++roman: For the Medusa, there's no need at all for that cache stuff,
-	 * because the hardware does bus snooping (fine!).
-	 */
-	dma_cache_maintenance( addr, count, dir );
-
-	if (count == 0)
-		printk(KERN_NOTICE "SCSI warning: DMA programmed for 0 bytes !\n");
-
-	mac_scsi_dma.dma_ctrl = dir;
-	SCSI_DMA_WRITE_P( dma_addr, addr );
-	SCSI_DMA_WRITE_P( dma_cnt, count );
-	mac_scsi_dma.dma_ctrl = dir | 2;
-
-	return( count );
-}
-
-
-static long mac_scsi_dma_residual( struct Scsi_Host *instance )
-{
-	return( mac_dma_residual );
-}
-
-
-#define	CMD_SURELY_BLOCK_MODE	0
-#define	CMD_SURELY_BYTE_MODE	1
-#define	CMD_MODE_UNKNOWN		2
-
-static int mac_classify_cmd( Scsi_Cmnd *cmd )
-{
-	unsigned char opcode = cmd->cmnd[0];
-	
-	if (opcode == READ_DEFECT_DATA || opcode == READ_LONG ||
-		opcode == READ_BUFFER)
-		return( CMD_SURELY_BYTE_MODE );
-	else if (opcode == READ_6 || opcode == READ_10 ||
-		 opcode == 0xa8 /* READ_12 */ || opcode == READ_REVERSE ||
-		 opcode == RECOVER_BUFFERED_DATA) {
-		/* In case of a sequential-access target (tape), special care is
-		 * needed here: The transfer is block-mode only if the 'fixed' bit is
-		 * set! */
-		if (cmd->device->type == TYPE_TAPE && !(cmd->cmnd[1] & 1))
-			return( CMD_SURELY_BYTE_MODE );
-		else
-			return( CMD_SURELY_BLOCK_MODE );
-	}
-	else
-		return( CMD_MODE_UNKNOWN );
-}
-
-#endif	/* REAL_DMA */
-
-#if defined(REAL_DMA) || defined(PSEUDO_DMA)
-/* This function calculates the number of bytes that can be transferred via
- * DMA. On the TT, this is arbitrary, but on the Falcon we have to use the
- * ST-DMA chip. There are only multiples of 512 bytes possible and max.
- * 255*512 bytes :-( This means also, that defining READ_OVERRUNS is not
- * possible on the Falcon, since that would require to program the DMA for
- * n*512 - atari_read_overrun bytes. But it seems that the Falcon doesn't have
- * the overrun problem, so this question is academic :-)
- */
-
-static unsigned long mac_dma_xfer_len( unsigned long wanted_len,
-					Scsi_Cmnd *cmd,
-					int write_flag )
-{
-	unsigned long	possible_len, limit;
-
-#if defined(REAL_DMA)
-	if (IS_A_TT())
-		/* TT SCSI DMA can transfer arbitrary #bytes */
-		return( wanted_len );
-
-	/* ST DMA chip is stupid -- only multiples of 512 bytes! (and max.
-	 * 255*512 bytes, but this should be enough)
-	 *
-	 * ++roman: Aaargl! Another Falcon-SCSI problem... There are some commands
-	 * that return a number of bytes which cannot be known beforehand. In this
-	 * case, the given transfer length is an "allocation length". Now it
-	 * can happen that this allocation length is a multiple of 512 bytes and
-	 * the DMA is used. But if not n*512 bytes really arrive, some input data
-	 * will be lost in the ST-DMA's FIFO :-( Thus, we have to distinguish
-	 * between commands that do block transfers and those that do byte
-	 * transfers. But this isn't easy... there are lots of vendor specific
-	 * commands, and the user can issue any command via the
-	 * SCSI_IOCTL_SEND_COMMAND.
-	 *
-	 * The solution: We classify SCSI commands in 1) surely block-mode cmd.s,
-	 * 2) surely byte-mode cmd.s and 3) cmd.s with unknown mode. In case 1)
-	 * and 3), the thing to do is obvious: allow any number of blocks via DMA
-	 * or none. In case 2), we apply some heuristic: Byte mode is assumed if
-	 * the transfer (allocation) length is < 1024, hoping that no cmd. not
-	 * explicitly known as byte mode have such big allocation lengths...
-	 * BTW, all the discussion above applies only to reads. DMA writes are
-	 * unproblematic anyways, since the targets aborts the transfer after
-	 * receiving a sufficient number of bytes.
-	 *
-	 * Another point: If the transfer is from/to an non-ST-RAM address, we
-	 * use the dribble buffer and thus can do only STRAM_BUFFER_SIZE bytes.
-	 */
-
-	if (write_flag) {
-		/* Write operation can always use the DMA, but the transfer size must
-		 * be rounded up to the next multiple of 512 (atari_dma_setup() does
-		 * this).
-		 */
-		possible_len = wanted_len;
-	}
-	else {
-		/* Read operations: if the wanted transfer length is not a multiple of
-		 * 512, we cannot use DMA, since the ST-DMA cannot split transfers
-		 * (no interrupt on DMA finished!)
-		 */
-		if (wanted_len & 0x1ff)
-			possible_len = 0;
-		else {
-			/* Now classify the command (see above) and decide whether it is
-			 * allowed to do DMA at all */
-			switch( falcon_classify_cmd( cmd )) {
-			  case CMD_SURELY_BLOCK_MODE:
-				possible_len = wanted_len;
-				break;
-			  case CMD_SURELY_BYTE_MODE:
-				possible_len = 0; /* DMA prohibited */
-				break;
-			  case CMD_MODE_UNKNOWN:
-			  default:
-				/* For unknown commands assume block transfers if the transfer
-				 * size/allocation length is >= 1024 */
-				possible_len = (wanted_len < 1024) ? 0 : wanted_len;
-				break;
-			}
-		}
-	}
-
-	/* Last step: apply the hard limit on DMA transfers */
-	limit = (atari_dma_buffer && !STRAM_ADDR( VTOP(cmd->SCp.ptr) )) ?
-		    STRAM_BUFFER_SIZE : 255*512;
-	if (possible_len > limit)
-		possible_len = limit;
-
-	if (possible_len != wanted_len)
-		DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes "
-			   "instead of %ld\n", possible_len, wanted_len);
-
-#else /* REAL_DMA */
-	possible_len = wanted_len;
-#endif
-	
-	return( possible_len );
-}
-#endif	/* REAL_DMA || PSEUDO_DMA */
-
-
-/* 
- * FIXME !!!
- */
-
-/* NCR5380 register access functions
- */
-
-static unsigned char mac_scsi_reg_read( unsigned char reg )
-{
-	return( mac_scsi_regp[reg << 4] );
-}
-
-static void mac_scsi_reg_write( unsigned char reg, unsigned char value )
-{
-	mac_scsi_regp[reg << 4] = value;
-}
-
-#include "mac_NCR5380.c"
-
-#ifdef PSEUDO_DMA
-
 /*
- * slightly optimized PIO transfer routines, experimental!
- * command may time out if interrupts are left enabled
+ * NCR 5380 register access functions
  */
 
-static inline int mac_pdma_read (struct Scsi_Host *instance, unsigned char *dst,    int len)
-{
-    register unsigned char *d = dst;
-    register i = len;
-    register unsigned char p, tmp;
-
-#if (NDEBUG & NDEBUG_PSEUDO_DMA)
-    printk("pdma_read: reading %d bytes to %p\n", len, dst);
-#endif
-
-    mac_pdma_residual = len;
-    if (mac_pdma_active) {
-      printk("pseudo-DMA already active in pread!\n");
-      return -1;
-    }
-    mac_pdma_active = 1;
-    mac_pdma_startaddr = (unsigned long) dst;
-    mac_pdma_current   = (unsigned long) dst;
-
-    /* 
-     * Get the phase from the bus (sanity check) 
-     * Hopefully, the phase bits are valid here ...
-     */
-    p = NCR5380_read(STATUS_REG) & PHASE_MASK;
-    if (!(p & SR_IO)) {
-      PDMA_PRINTK("NCR5380_pread: initial phase mismatch!\n");
-      NCR_PRINT_PHASE(NDEBUG_ANY);
-      return -1;
-    }
-
-    /* 
-     * The NCR5380 chip will only drive the SCSI bus when the 
-     * phase specified in the appropriate bits of the TARGET COMMAND
-     * REGISTER match the STATUS REGISTER
-     */
-
-#if 0	/* done in transfer_dma */
-    p = PHASE_DATAIN;
-    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-#endif
-
-    for (; i; --i) {
-	HSH_PRINTK(" read %d ..", i);
-        /* check if we were interrupted ... */
-        if (!mac_pdma_active) {
-	  printk("pwrite: interrupt detected!\n");
-	  break;
-        }
-
-	/* 
-	 * Wait for assertion of REQ, after which the phase bits will be 
-	 * valid 
-	 */
-	while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ))
-		barrier();
-
-	HSH_PRINTK(" REQ ..");
-
-	/* Check for phase mismatch */	
-	if ((tmp & PHASE_MASK) != p) {
-            if (!mac_pdma_active) 
-	    	printk("scsi%d : phase mismatch after interrupt\n", instance->host_no);
-	    else
-	    	printk("scsi%d : phase mismatch w/o interrupt\n", instance->host_no);
-	    NCR_PRINT_PHASE(NDEBUG_ANY);
-	    break;
-	}
+#define CTRL(p,v) (*ctrl = (v))
 
-	/* Do actual transfer from SCSI bus to memory */
-	*d = NCR5380_read(CURRENT_SCSI_DATA_REG);
-
-	d++;
-	
-	/* Handshake ... */
-
-	/* Assert ACK */
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
-	HSH_PRINTK(" ACK ..");
-
-	/* Wait for REQ to be dropped */
-	while (NCR5380_read(STATUS_REG) & SR_REQ)
-		barrier();
-	HSH_PRINTK(" /REQ ..");
-
-	/* Drop ACK */
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-	HSH_PRINTK(" /ACK !\n");
-
-	mac_pdma_current = (unsigned long) d;
-	mac_pdma_residual--;
-    }
-
-#if (NDEBUG & NDEBUG_PSEUDO_DMA)
-    printk("pdma_read: read at %d bytes to %p\n", i, dst);
-#endif
-
-    if (mac_pdma_residual)
-      printk("pread: leaving with residual %ld of %ld\n", 
-      		mac_pdma_residual, len);
-    mac_pdma_active = 0;
-
-    /* ?? */
-#if 0
-    NCR5380_write(MODE_REG, MR_BASE);
-    NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-#endif
-    return 0;
-}
-		
-static inline int mac_pdma_write (struct Scsi_Host *instance, unsigned char *src,    int len)
+static char macscsi_read(struct Scsi_Host *instance, int reg)
 {
-    register unsigned char *s = src;
-    register i = len;
-    register unsigned char p, tmp;
+  int iobase = instance->io_port;
+  int i;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(iobase, 0);
+  i = inb(iobase + (reg<<4));
+  CTRL(iobase, 0x40);
+
+  return i;
+}
+
+static void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int iobase = instance->io_port;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(iobase, 0);
+  outb(value, iobase + (reg<<4));
+  CTRL(iobase, 0x40);
+}
+
+/* Pseudo-DMA stuff */
+
+static inline void cp_io_to_mem(volatile unsigned char *s, volatile unsigned char *d, int len)
+{
+  asm volatile("   cmp.w  #4,%2; \
+                   ble    8f; \
+                   move.w %1,%%d0; \
+                   neg.b  %%d0; \
+                   and.w  #3,%%d0; \
+                   sub.w  %%d0,%2; \
+                   bra    2f; \
+                1: move.b (%0),(%1)+; \
+                2: dbf    %%d0,1b; \
+                   move.w %2,%%d0; \
+                   lsr.w  #5,%%d0; \
+                   bra    4f; \
+                3: move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                   move.l (%0),(%1)+; \
+                4: dbf    %%d0,3b; \
+                   move.w %2,%%d0; \
+                   lsr.w  #2,%%d0; \
+                   and.w  #7,%%d0; \
+                   bra    6f; \
+                5: move.l (%0),(%1)+; \
+                6: dbf    %%d0,5b; \
+                   and.w  #3,%2
+                   bra    8f; \
+                7: move.b (%0),(%1)+; \
+                8: dbf    %2,7b"
+               :: "a" (s), "a" (d), "d" (len)
+               :  "%%d0");
+}
+
+static inline int macscsi_pread (struct Scsi_Host *instance,
+				 unsigned char *dst, int len)
+{
+   unsigned char *d;
+   volatile unsigned char *s;
+
+   NCR5380_local_declare();
+   NCR5380_setup(instance);
+
+   s = mac_scsi_drq+0x60;
+   d = dst;
+
+/* These conditions are derived from MacOS. (How to read CONTROL_STATUS_REG?) */
+
+   while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) 
+         && !(NCR5380_read(STATUS_REG) & SR_REQ))
+      ;
+   if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) 
+         && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+      printk("Error in macscsi_pread\n");
+      return 5;
+   }
+
+   cp_io_to_mem(s, d, len);
+   
+   return 0;
+}
+
+
+
+static inline void cp_mem_to_io(volatile unsigned char *s, volatile unsigned char *d, int len)
+{
+  asm volatile("   cmp.w  #4,%2; \
+                   ble    8f; \
+                   move.w %0,%%d0; \
+                   neg.b  %%d0; \
+                   and.w  #3,%%d0; \
+                   sub.w  %%d0,%2; \
+                   bra    2f; \
+                1: move.b (%0)+,(%1); \
+                2: dbf    %%d0,1b; \
+                   move.w %2,%%d0; \
+                   lsr.w  #5,%%d0; \
+                   bra    4f; \
+                3: move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                   move.l (%0)+,(%1); \
+                4: dbf    %%d0,3b; \
+                   move.w %2,%%d0; \
+                   lsr.w  #2,%%d0; \
+                   and.w  #7,%%d0; \
+                   bra    6f; \
+                5: move.l (%0)+,(%1); \
+                6: dbf    %%d0,5b; \
+                   and.w  #3,%2
+                   bra    8f; \
+                7: move.b (%0)+,(%1); \
+                8: dbf    %2,7b"
+               :: "a" (s), "a" (d), "d" (len)
+               :  "%%d0");
+}
+
+static inline int macscsi_pwrite (struct Scsi_Host *instance,
+				  unsigned char *src, int len)
+{
+   unsigned char *d;
+
+   NCR5380_local_declare();
+   NCR5380_setup(instance);
+
+   d = mac_scsi_drq;
+   
+/* These conditions are derived from MacOS. (How to read CONTROL_STATUS_REG?) */
+
+   while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) 
+         && !(NCR5380_read(STATUS_REG) & SR_REQ) 
+         && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) 
+      ;
+   if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) {
+      printk("Error in macscsi_pwrite\n");
+      return 5;
+   }
 
-#if (NDEBUG & NDEBUG_PSEUDO_DMA)
-    printk("pdma_write: writing %d bytes from %p\n", len, src);
-#endif
-
-    mac_pdma_residual = len;
-    if (mac_pdma_active) {
-      printk("pseudo-DMA already active in pwrite!\n");
-      return -1;
-    }
-    mac_pdma_active = 1;
-    mac_pdma_startaddr = (unsigned long) src;
-    mac_pdma_current   = (unsigned long) src;
-
-    /* 
-     * Get the phase from the bus (sanity check) 
-     */
-    p = NCR5380_read(STATUS_REG) & PHASE_MASK;
-    if (p & SR_IO) {
-      printk("NCR5380_pwrite: initial phase mismatch!\n");
-      NCR_PRINT_PHASE(NDEBUG_ANY);
-      return -1;
-    }
-
-    /* 
-     * The NCR5380 chip will only drive the SCSI bus when the 
-     * phase specified in the appropriate bits of the TARGET COMMAND
-     * REGISTER match the STATUS REGISTER
-     */
-
-#if 0	/* already done in transfer_dma */ 
-    p = PHASE_DATAOUT;
-    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-#endif
+   cp_mem_to_io(src, d, len);   
 
-    for (; i; --i) {
-        /* check if we were interrupted ... */
-        if (!mac_pdma_active) {
-	  printk("pwrite: interrupt detected!\n");
-	  break;
-        }
-
-       	/* 
-	 * Wait for assertion of REQ, after which the phase bits will be 
-	 * valid 
-	 */
-	while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
-
-	/* Check for phase mismatch */	
-	if ((tmp & PHASE_MASK) != p) {
-            if (!mac_pdma_active) 
-	    	printk("scsi%d : phase mismatch after interrupt\n", instance->host_no);
-	    else
-	    	printk("scsi%d : phase mismatch w/o interrupt\n", instance->host_no);
-	    NCR_PRINT_PHASE(NDEBUG_ANY);
-	    /* should we signal an error here?? */
-	    break;
-	}
-
-	/* Do actual transfer to SCSI bus from memory */
-
-	NCR5380_write(OUTPUT_DATA_REG, *s);
-
-	s++;
-	
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
-		      ICR_ASSERT_DATA);
-
-	/* Handshake ... assert ACK */
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
-		      ICR_ASSERT_DATA | ICR_ASSERT_ACK);
-
-	/* ... wait for REQ to be dropped */
-	while (NCR5380_read(STATUS_REG) & SR_REQ);
-
-	/* and drop ACK (and DATA) ! */
-	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-	mac_pdma_current = (unsigned long) s;
-	mac_pdma_residual--;
-    }
-
-#if (NDEBUG & NDEBUG_PSEUDO_DMA)
-    printk("pdma_write: write at %d bytes from %p\n", i, src);
-#endif
-
-    if (mac_pdma_residual)
-      printk("pwrite: leaving with residual %ld of len %ld \n", 
-      		mac_pdma_residual, len);
-    mac_pdma_active = 0;
+   return 0;
+}
 
-    return 0;
+/* These control the behaviour of the generic 5380 core */
+#define AUTOSENSE
+#undef DMA_WORKS_RIGHT
+#define PSEUDO_DMA
 
-}
-#endif /* PSEUDO_DMA */
+#include "NCR5380.c"
 
 #ifdef MODULE
-Scsi_Host_Template driver_template = MAC_SCSI;
+
+Scsi_Host_Template driver_template = MAC_NCR5380;
 
 #include "scsi_module.c"
 #endif
diff -urN linux-2.2.18-pre15/drivers/scsi/mac_scsi.h linux/drivers/scsi/mac_scsi.h
--- linux-2.2.18-pre15/drivers/scsi/mac_scsi.h	Thu Jul 30 11:17:10 1998
+++ linux/drivers/scsi/mac_scsi.h	Mon Dec  3 20:34:11 2001
@@ -1,286 +1,111 @@
 /*
- * mac_scsi.h -- Header file for the Macintosh native SCSI driver
+ * Cumana Generic NCR5380 driver defines
  *
- * based on Roman Hodeks atari_scsi.h
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
  */
 
 /*
- * atari_scsi.h -- Header file for the Atari native SCSI driver
- *
- * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
- *
- * (Loosely based on the work of Robert De Vries' team)
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive
- * for more details.
- *
+ * $Log: cumana_NCR5380.h,v $
  */
 
+#ifndef MAC_NCR5380_H
+#define MAC_NCR5380_H
 
-#ifndef MAC_SCSI_H
-#define MAC_SCSI_H
+#define MACSCSI_PUBLIC_RELEASE 1
 
-/* (I_HAVE_OVERRUNS stuff removed) */
 
 #ifndef ASM
-int mac_scsi_abort (Scsi_Cmnd *);
-int mac_scsi_detect (Scsi_Host_Template *);
-const char * mac_scsi_info (struct Scsi_Host *host);
-int mac_scsi_queue_command (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
-int mac_scsi_reset (Scsi_Cmnd *, unsigned int);
-int mac_scsi_proc_info (char *, char **, off_t, int, int, int);
-#ifdef MODULE
-int mac_scsi_release (struct Scsi_Host *);
-#else
-#define mac_scsi_release NULL
+int macscsi_abort (Scsi_Cmnd *);
+int macscsi_detect (Scsi_Host_Template *);
+int macscsi_release (struct Scsi_Host *);
+int macscsi_reset(Scsi_Cmnd *, unsigned int);
+int macscsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int macscsi_proc_info (char *buffer, char **start, off_t offset,
+			int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
 #endif
 
-/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher
- * values should work, too; try it! (but cmd_per_lun costs memory!) */
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
 
-/* But there seems to be a bug somewhere that requires CAN_QUEUE to be
- * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
- * changed CMD_PER_LUN... */
-
-/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with
- * cmd_per_lun != 1 */
-
-#define MAC_SCSI_CAN_QUEUE		        16
-#define MAC_SCSI_CMD_PER_LUN		        8
-#define MAC_SCSI_SG_TABLESIZE		SG_ALL
-
-#define	DEFAULT_USE_TAGGED_QUEUING	0
-
-
-#if defined (HOSTS_C) || defined (MODULE)
-
-#define MAC_SCSI { NULL, NULL, NULL,				\
-  mac_scsi_proc_info,						\
-  "Macintosh NCR5380 SCSI",					\
-  mac_scsi_detect,						\
-  mac_scsi_release,						\
-  mac_scsi_info,						\
-  /* command */ NULL,						\
-  mac_scsi_queue_command,					\
-  mac_scsi_abort,						\
-  mac_scsi_reset,						\
-  /* slave_attach */	NULL,					\
-  /* bios_param */	NULL,					\
-  /* can queue */	0, /* initialized at run-time */	\
-  /* host_id */		0, /* initialized at run-time */	\
-  /* scatter gather */	0, /* initialized at run-time */	\
-  /* cmd per lun */	0, /* initialized at run-time */	\
-  /* present */		0,					\
-  /* unchecked ISA DMA */ 0,					\
-  /* use_clustering */	DISABLE_CLUSTERING }
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
 
+#ifndef SG_TABLESIZE
+#define SG_TABLESIZE SG_NONE
 #endif
 
-#ifndef HOSTS_C
+#ifndef USE_TAGGED_QUEUING
+#define	USE_TAGGED_QUEUING 0
+#endif
 
-#define	NCR5380_implementation_fields	/* none */
+#include <scsi/scsicam.h>
 
-#define NCR5380_read(reg)		  mac_scsi_reg_read( reg )
-#define NCR5380_write(reg, value) mac_scsi_reg_write( reg, value )
+#define MAC_NCR5380 {						\
+name:			"Macintosh NCR5380 SCSI",			\
+detect:			macscsi_detect,					\
+release:		macscsi_release,	/* Release */		\
+queuecommand:		macscsi_queue_command,				\
+abort:			macscsi_abort,			 		\
+reset:			macscsi_reset,					\
+bios_param:		scsicam_bios_param,	/* biosparam */		\
+can_queue:		CAN_QUEUE,		/* can queue */		\
+this_id:		7,			/* id */		\
+sg_tablesize:		SG_ALL,			/* sg_tablesize */	\
+cmd_per_lun:		CMD_PER_LUN,		/* cmd per lun */	\
+unchecked_isa_dma:	0,			/* unchecked_isa_dma */	\
+use_clustering:		DISABLE_CLUSTERING				\
+	}
 
-#define NCR5380_intr mac_scsi_intr
-#define NCR5380_queue_command mac_scsi_queue_command
-#define NCR5380_abort mac_scsi_abort
-#define NCR5380_proc_info mac_scsi_proc_info
-#define NCR5380_dma_read_setup(inst,d,c) mac_scsi_dma_setup (inst, d, c, 0)
-#define NCR5380_dma_write_setup(inst,d,c) mac_scsi_dma_setup (inst, d, c, 1)
-#define NCR5380_dma_residual(inst) mac_scsi_dma_residual( inst )
-#define	NCR5380_dma_xfer_len(i,cmd,phase) \
-	mac_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
-#ifdef PSEUDO_DMA
-#define NCR5380_pread(inst,d,l) mac_pdma_read (inst, d, l)
-#define NCR5380_pwrite(inst,d,l) mac_pdma_write (inst, d, l)
-#endif
+#ifndef HOSTS_C
 
-/* Debugging printk definitions:
- *
- *  ARB  -> arbitration
- *  ASEN -> auto-sense
- *  DMA  -> DMA
- *  HSH  -> PIO handshake
- *  INF  -> information transfer
- *  INI  -> initialization
- *  INT  -> interrupt
- *  LNK  -> linked commands
- *  MAIN -> NCR5380_main() control flow
- *  NDAT -> no data-out phase
- *  NWR  -> no write commands
- *  PIO  -> PIO transfers
- *  PDMA -> pseudo DMA (unused on MAC)
- *  QU   -> queues
- *  RSL  -> reselections
- *  SEL  -> selections
- *  USL  -> usleep cpde (unused on MAC)
- *  LBS  -> last byte sent (unused on MAC)
- *  RSS  -> restarting of selections
- *  EXT  -> extended messages
- *  ABRT -> aborting and resetting
- *  TAG  -> queue tag handling
- *  MER  -> merging of consec. buffers
- *
- */
+#define NCR5380_implementation_fields \
+    int port, ctrl
 
-#if NDEBUG & NDEBUG_ARBITRATION
-#define ARB_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define ARB_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_AUTOSENSE
-#define ASEN_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define ASEN_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_DMA
-#define DMA_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define DMA_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_HANDSHAKE
-#define HSH_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define HSH_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_INFORMATION
-#define INF_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define INF_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_INIT
-#define INI_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define INI_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_INTR
-#define INT_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define INT_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_LINKED
-#define LNK_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define LNK_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_MAIN
-#define MAIN_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define MAIN_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_NO_DATAOUT
-#define NDAT_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define NDAT_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_NO_WRITE
-#define NWR_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define NWR_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_PIO
-#define PIO_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define PIO_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_PSEUDO_DMA
-#define PDMA_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define PDMA_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_QUEUES
-#define QU_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define QU_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_RESELECTION
-#define RSL_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define RSL_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_SELECTION
-#define SEL_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define SEL_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_USLEEP
-#define USL_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define USL_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_LAST_BYTE_SENT
-#define LBS_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define LBS_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_RESTART_SELECT
-#define RSS_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define RSS_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_EXTENDED
-#define EXT_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define EXT_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_ABORT
-#define ABRT_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define ABRT_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_TAGS
-#define TAG_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define TAG_PRINTK(format, args...)
-#endif
-#if NDEBUG & NDEBUG_MERGING
-#define MER_PRINTK(format, args...) \
-	printk(KERN_DEBUG format , ## args)
-#else
-#define MER_PRINTK(format, args...)
-#endif
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
 
-/* conditional macros for NCR5380_print_{,phase,status} */
+#define NCR5380_setup(instance) \
+        _instance = instance
 
-#define NCR_PRINT(mask)	\
-	((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
+#define NCR5380_read(reg) macscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
 
-#define NCR_PRINT_PHASE(mask) \
-	((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
+#define NCR5380_pread macscsi_pread
+#define NCR5380_pwrite macscsi_pwrite
 
-#define NCR_PRINT_STATUS(mask) \
-	((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
+#define NCR5380_intr macscsi_intr
+#define NCR5380_queue_command macscsi_queue_command
+#define NCR5380_abort macscsi_abort
+#define NCR5380_reset macscsi_reset
+#define NCR5380_proc_info macscsi_proc_info
 
-#define NDEBUG_ANY	0xffffffff
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
 
-
-#endif /* else def HOSTS_C */
+#endif /* ndef HOSTS_C */
 #endif /* ndef ASM */
-#endif /* MAC_SCSI_H */
-
+#endif /* MAC_NCR5380_H */
 
diff -urN linux-2.2.18-pre15/drivers/scsi/oktagon_esp.c linux/drivers/scsi/oktagon_esp.c
--- linux-2.2.18-pre15/drivers/scsi/oktagon_esp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/oktagon_esp.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,598 @@
+/*
+ * Oktagon_esp.c -- Driver for bsc Oktagon
+ *
+ * Written by Carsten Pluntke 1998
+ *
+ * Based on cyber_esp.c
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_AMIGA) || defined(CONFIG_APUS)
+#define USE_BOTTOM_HALF
+#endif
+
+#define __KERNEL_SYSCALLS__
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/reboot.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "NCR53C9x.h"
+#include "oktagon_esp.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+#ifdef USE_BOTTOM_HALF
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#endif
+
+#include <linux/unistd.h>
+
+static int  dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int  dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int  dma_irq_p(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int  dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static void dma_irq_exit(struct NCR_ESP *esp);
+static void dma_invalidate(struct NCR_ESP *esp);
+
+static void dma_mmu_get_scsi_one(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_get_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_release_scsi_one(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_release_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_advance_sg(Scsi_Cmnd *);
+static int  oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x);
+
+void esp_bootup_reset(struct NCR_ESP *esp,struct ESP_regs *eregs);
+
+#ifdef USE_BOTTOM_HALF
+static void dma_commit(void *opaque);
+
+long oktag_to_io(long *paddr, long *addr, long len);
+long oktag_from_io(long *addr, long *paddr, long len);
+
+static struct tq_struct tq_fake_dma = { NULL, 0, dma_commit, NULL };
+
+#define DMA_MAXTRANSFER 0x8000
+
+#else
+
+/*
+ * No bottom half. Use transfer directly from IRQ. Find a narrow path
+ * between too much IRQ overhead and clogging the IRQ for too long.
+ */
+
+#define DMA_MAXTRANSFER 0x1000
+
+#endif
+
+static struct notifier_block oktagon_notifier = { 
+	oktagon_notify_reboot,
+	NULL,
+	0
+};
+
+static long *paddress;
+static long *address;
+static long len;
+static long dma_on;
+static int direction;
+static struct NCR_ESP *current_esp;
+
+
+volatile unsigned char cmd_buffer[16];
+				/* This is where all commands are put
+				 * before they are trasfered to the ESP chip
+				 * via PIO.
+				 */
+
+/***************************************************************** Detection */
+int oktagon_esp_detect(Scsi_Host_Template *tpnt)
+{
+	struct NCR_ESP *esp;
+	const struct ConfigDev *esp_dev;
+	int key;
+	unsigned long address;
+	struct ESP_regs *eregs;
+
+	if((key = zorro_find(ZORRO_PROD_BSC_OKTAGON_2008, 0, 0))){
+		esp_dev = zorro_get_board(key);
+
+		/*
+		 * It is a SCSI controller.
+		 * Hardwire Host adapter to SCSI ID 7
+		 */
+		
+		address = (unsigned long)ZTWO_VADDR(esp_dev->cd_BoardAddr);
+		eregs = (struct ESP_regs *)(address + OKTAGON_ESP_ADDR);
+
+		/* This line was 5 lines lower */
+		esp = esp_allocate(tpnt, (void *) esp_dev);
+
+		/* we have to shift the registers only one bit for oktagon */
+		esp->shift = 1;
+
+		esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7));
+		udelay(5);
+		if (esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7))
+			return 0; /* Bail out if address did not hold data */
+
+		/* Do command transfer with programmed I/O */
+		esp->do_pio_cmds = 1;
+
+		/* Required functions */
+		esp->dma_bytes_sent = &dma_bytes_sent;
+		esp->dma_can_transfer = &dma_can_transfer;
+		esp->dma_dump_state = &dma_dump_state;
+		esp->dma_init_read = &dma_init_read;
+		esp->dma_init_write = &dma_init_write;
+		esp->dma_ints_off = &dma_ints_off;
+		esp->dma_ints_on = &dma_ints_on;
+		esp->dma_irq_p = &dma_irq_p;
+		esp->dma_ports_p = &dma_ports_p;
+		esp->dma_setup = &dma_setup;
+
+		/* Optional functions */
+		esp->dma_barrier = 0;
+		esp->dma_drain = 0;
+		esp->dma_invalidate = &dma_invalidate;
+		esp->dma_irq_entry = 0;
+		esp->dma_irq_exit = &dma_irq_exit;
+		esp->dma_led_on = &dma_led_on;
+		esp->dma_led_off = &dma_led_off;
+		esp->dma_poll = 0;
+		esp->dma_reset = 0;
+
+		esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+		esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+		esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
+		esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
+		esp->dma_advance_sg = &dma_advance_sg;
+
+		/* SCSI chip speed */
+		/* Looking at the quartz of the SCSI board... */
+		esp->cfreq = 25000000;
+
+		/* The DMA registers on the CyberStorm are mapped
+		 * relative to the device (i.e. in the same Zorro
+		 * I/O block).
+		 */
+		esp->dregs = (void *)(address + OKTAGON_DMA_ADDR);
+
+		paddress = (long *) esp->dregs;
+
+		/* ESP register base */
+		esp->eregs = eregs;
+		
+		/* Set the command buffer */
+		esp->esp_command = (volatile unsigned char*) cmd_buffer;
+
+		/* Yes, the virtual address. See below. */
+		esp->esp_command_dvma = (__u32) cmd_buffer;
+
+		esp->irq = IRQ_AMIGA_PORTS;
+		esp->slot = key;
+		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, "BSC Oktagon SCSI", esp_intr);
+
+		/* Figure out our scsi ID on the bus */
+		esp->scsi_id = 7;
+		
+		/* We don't have a differential SCSI-bus. */
+		esp->diff = 0;
+
+		esp_initialize(esp);
+
+		zorro_config_board(key, 0);
+		printk("ESP_Oktagon Driver 1.1"
+#ifdef USE_BOTTOM_HALF
+		       " [BOTTOM_HALF]"
+#else
+		       " [IRQ]"
+#endif
+		       " registered.\n");
+		printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use);
+		esps_running = esps_in_use;
+		current_esp = esp;
+		register_reboot_notifier(&oktagon_notifier);
+		return esps_in_use;
+	}
+	return 0;
+}
+
+
+/*
+ * On certain configurations the SCSI equipment gets confused on reboot,
+ * so we have to reset it then.
+ */
+
+static int
+oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+{
+  struct NCR_ESP *esp;
+  
+  if((code == SYS_DOWN || code == SYS_HALT) && (esp = current_esp))
+   {
+    esp_bootup_reset(esp,esp->eregs);
+    udelay(500); /* Settle time. Maybe unneccessary. */
+   }
+  return NOTIFY_DONE;
+}
+    
+
+	
+#ifdef USE_BOTTOM_HALF
+
+
+/*
+ * The bsc Oktagon controller has no real DMA, so we have to do the 'DMA
+ * transfer' in the interrupt (Yikes!) or use a bottom half to not to clutter
+ * IRQ's for longer-than-good.
+ *
+ * FIXME
+ * BIG PROBLEM: 'len' is usually the buffer length, not the expected length
+ * of the data. So DMA may finish prematurely, further reads lead to
+ * 'machine check' on APUS systems (don't know about m68k systems, AmigaOS
+ * deliberately ignores the bus faults) and a normal copy-loop can't
+ * be exited prematurely just at the right moment by the dma_invalidate IRQ.
+ * So do it the hard way, write an own copier in assembler and
+ * catch the exception.
+ *                                     -- Carsten
+ */
+ 
+ 
+static void dma_commit(void *opaque)
+{
+    long wait,len2,pos;
+    struct NCR_ESP *esp;
+
+    ESPDATA(("Transfer: %ld bytes, Address 0x%08lX, Direction: %d\n",
+         len,(long) address,direction));
+    dma_ints_off(current_esp);
+
+    pos = 0;
+    wait = 1;
+    if(direction) /* write? (memory to device) */
+     {
+      while(len > 0)
+       {
+        len2 = oktag_to_io(paddress, address+pos, len);
+	if(!len2)
+	 {
+	  if(wait > 1000)
+	   {
+	    printk("Expedited DMA exit (writing) %ld\n",len);
+	    break;
+	   }
+	  mdelay(wait);
+	  wait *= 2;
+	 }
+	pos += len2;
+	len -= len2*sizeof(long);
+       }
+     } else {
+      while(len > 0)
+       {
+        len2 = oktag_from_io(address+pos, paddress, len);
+	if(!len2)
+	 {
+	  if(wait > 1000)
+	   {
+	    printk("Expedited DMA exit (reading) %ld\n",len);
+	    break;
+	   }
+	  mdelay(wait);
+	  wait *= 2;
+	 }
+	pos += len2;
+	len -= len2*sizeof(long);
+       }
+     }
+
+    /* to make esp->shift work */
+    esp=current_esp;
+
+#if 0
+    len2 = (esp_read(current_esp->eregs->esp_tclow) & 0xff) |
+           ((esp_read(current_esp->eregs->esp_tcmed) & 0xff) << 8);
+
+    /*
+     * Uh uh. If you see this, len and transfer count registers were out of
+     * sync. That means really serious trouble.
+     */
+
+    if(len2)
+      printk("Eeeek!! Transfer count still %ld!\n",len2);
+#endif
+
+    /*
+     * Normally we just need to exit and wait for the interrupt to come.
+     * But at least one device (my Microtek ScanMaker 630) regularly mis-
+     * calculates the bytes it should send which is really ugly because
+     * it locks up the SCSI bus if not accounted for.
+     */
+
+    if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+     {
+      long len = 100;
+      long trash[10];
+
+      /*
+       * Interrupt bit was not set. Either the device is just plain lazy
+       * so we give it a 10 ms chance or...
+       */
+      while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)))
+        udelay(100);
+
+
+      if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+       {
+        /*
+	 * So we think that the transfer count is out of sync. Since we
+	 * have all we want we are happy and can ditch the trash.
+	 */
+	 
+        len = DMA_MAXTRANSFER;
+
+        while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)))
+          oktag_from_io(trash,paddress,2);
+
+        if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+         {
+          /*
+           * Things really have gone wrong. If we leave the system in that
+           * state, the SCSI bus is locked forever. I hope that this will
+           * turn the system in a more or less running state.
+           */
+          printk("Device is bolixed, trying bus reset...\n");
+	  esp_bootup_reset(current_esp,current_esp->eregs);
+         }
+       }
+     }
+
+    ESPDATA(("Transfer_finale: do_data_finale should come\n"));
+
+    len = 0;
+    dma_on = 0;
+    dma_ints_on(current_esp);
+}
+
+#endif
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+	/* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+	 * the number of bytes sent (to the ESP chip) equals the number
+	 * of bytes in the FIFO - there is no buffering in the DMA controller.
+	 * XXXX Do I read this right? It is from host to ESP, right?
+	 */
+	return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+	unsigned long sz = sp->SCp.this_residual;
+	if(sz > DMA_MAXTRANSFER)
+		sz = DMA_MAXTRANSFER;
+	return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+}
+
+/*
+ * What the f$@& is this?
+ *
+ * Some SCSI devices (like my Microtek ScanMaker 630 scanner) want to transfer
+ * more data than requested. How much? Dunno. So ditch the bogus data into
+ * the sink, hoping the device will advance to the next phase sooner or later.
+ *
+ *                         -- Carsten
+ */
+
+static long oktag_eva_buffer[16]; /* The data sink */
+
+static void oktag_check_dma(void)
+{
+  struct NCR_ESP *esp;
+
+  esp=current_esp;
+  if(!len)
+   {
+    address = oktag_eva_buffer;
+    len = 2;
+    /* esp_do_data sets them to zero like len */
+    esp_write(current_esp->eregs->esp_tclow,2);
+    esp_write(current_esp->eregs->esp_tcmed,0);
+   }
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+	/* Zorro is noncached, everything else done using processor. */
+	/* cache_clear(addr, length); */
+	
+	if(dma_on)
+	  panic("dma_init_read while dma process is initialized/running!\n");
+	direction = 0;
+	address = (long *) vaddress;
+	current_esp = esp;
+	len = length;
+	oktag_check_dma();
+        dma_on = 1;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+	/* cache_push(addr, length); */
+
+	if(dma_on)
+	  panic("dma_init_write while dma process is initialized/running!\n");
+	direction = 1;
+	address = (long *) vaddress;
+	current_esp = esp;
+	len = length;
+	oktag_check_dma();
+	dma_on = 1;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+	disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+	enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+	/* It's important to check the DMA IRQ bit in the correct way! */
+	return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+	return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+	/* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+	 * so when (write) is true, it actually means READ!
+	 */
+	if(write){
+		dma_init_read(esp, addr, count);
+	} else {
+		dma_init_write(esp, addr, count);
+	}
+}
+
+/*
+ * IRQ entry when DMA transfer is ready to be started
+ */
+
+static void dma_irq_exit(struct NCR_ESP *esp)
+{
+#ifdef USE_BOTTOM_HALF
+	if(dma_on)
+	 {
+	  tq_fake_dma.sync = 0;
+	  queue_task(&tq_fake_dma,&tq_immediate);
+	  mark_bh(IMMEDIATE_BH);
+	 }
+#else
+	while(len && !dma_irq_p(esp))
+	 {
+	  if(direction)
+	    *paddress = *address++;
+	   else
+	    *address++ = *paddress;
+	  len -= (sizeof(long));
+	 }
+	len = 0;
+        dma_on = 0;
+#endif
+}
+
+/*
+ * IRQ entry when DMA has just finished
+ */
+
+static void dma_invalidate(struct NCR_ESP *esp)
+{
+}
+
+/*
+ * Since the processor does the data transfer we have to use the custom
+ * mmu interface to pass the virtual address, not the physical.
+ */
+
+void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+        sp->SCp.have_data_in = (int) sp->SCp.ptr =
+                sp->request_buffer;
+}
+
+void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+        sp->SCp.ptr = 
+                sp->SCp.buffer->address;
+}
+
+void dma_mmu_release_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+}
+
+void dma_mmu_release_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+}
+
+void dma_advance_sg(Scsi_Cmnd *sp)
+{
+  sp->SCp.ptr = sp->SCp.buffer->address;
+}
+
+#ifdef MODULE
+
+#define HOSTS_C
+
+#include "oktagon_esp.h"
+
+Scsi_Host_Template driver_template = SCSI_OKTAGON_ESP;
+
+#include "scsi_module.c"
+
+#endif
+
+int oktagon_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+	unsigned int key;
+
+	key = ((struct NCR_ESP *)instance->hostdata)->slot;
+	esp_release();
+	zorro_unconfig_board(key, 0);
+	free_irq(IRQ_AMIGA_PORTS, esp_intr);
+	unregister_reboot_notifier(&oktagon_notifier);
+#endif
+	return 1;
+}
diff -urN linux-2.2.18-pre15/drivers/scsi/oktagon_esp.h linux/drivers/scsi/oktagon_esp.h
--- linux-2.2.18-pre15/drivers/scsi/oktagon_esp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/oktagon_esp.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,57 @@
+/* oktagon_esp.h: Defines and structures for the CyberStorm SCSI Mk II driver.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cs.auc.dk)
+ */
+
+#include "NCR53C9x.h"
+
+#ifndef OKTAGON_ESP_H
+#define OKTAGON_ESP_H
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define OKTAGON_ESP_ADDR 0x03000
+#define OKTAGON_DMA_ADDR 0x01000
+
+
+/* The CyberStorm II DMA interface */
+struct oktagon_dma_registers {
+	volatile unsigned char cond_reg;        /* DMA cond    (ro)  [0x000] */
+#define ctrl_reg  cond_reg			/* DMA control (wo)  [0x000] */
+	unsigned char dmapad4[0x3f];
+	volatile unsigned char dma_addr0;	/* DMA address (MSB) [0x040] */
+	unsigned char dmapad1[3];
+	volatile unsigned char dma_addr1;	/* DMA address       [0x044] */
+	unsigned char dmapad2[3];
+	volatile unsigned char dma_addr2;	/* DMA address       [0x048] */
+	unsigned char dmapad3[3];
+	volatile unsigned char dma_addr3;	/* DMA address (LSB) [0x04c] */
+};
+
+extern int oktagon_esp_detect(struct SHT *);
+extern int oktagon_esp_release(struct Scsi_Host *);
+extern const char *esp_info(struct Scsi_Host *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_command(Scsi_Cmnd *);
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+extern int esp_proc_info(char *buffer, char **start, off_t offset, int length,
+			int hostno, int inout);
+
+#define SCSI_OKTAGON_ESP {                       \
+   proc_dir:            &proc_scsi_esp,          \
+   proc_info:           &esp_proc_info,          \
+   name:                "BSC Oktagon SCSI",      \
+   detect:              oktagon_esp_detect,      \
+   release:             oktagon_esp_release,     \
+   queuecommand:        esp_queue,               \
+   abort:               esp_abort,               \
+   reset:               esp_reset,               \
+   can_queue:           7,                       \
+   this_id:             7,                       \
+   sg_tablesize:        SG_ALL,                  \
+   cmd_per_lun:         1,                       \
+   use_clustering:      ENABLE_CLUSTERING }
+
+#endif /* OKTAGON_ESP_H */
diff -urN linux-2.2.18-pre15/drivers/scsi/oktagon_io.S linux/drivers/scsi/oktagon_io.S
--- linux-2.2.18-pre15/drivers/scsi/oktagon_io.S	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/oktagon_io.S	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,195 @@
+/* -*- mode: asm -*-
+ * Due to problems while transferring data I've put these routines as assembly
+ * code.
+ * Since I'm no PPC assembler guru, the code is just the assembler version of
+
+int oktag_to_io(long *paddr,long *addr,long len)
+{
+  long *addr2 = addr;
+  for(len=(len+sizeof(long)-1)/sizeof(long);len--;)
+    *paddr = *addr2++;
+  return addr2 - addr;
+}
+
+int oktag_from_io(long *addr,long *paddr,long len)
+{
+  long *addr2 = addr;
+  for(len=(len+sizeof(long)-1)/sizeof(long);len--;)
+    *addr2++ = *paddr;
+  return addr2 - addr;
+}
+
+ * assembled using gcc -O2 -S, with two exception catch points where data
+ * is moved to/from the IO register.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_APUS
+
+	.file	"oktagon_io.c"
+
+gcc2_compiled.:
+/*
+	.section ".text"
+*/
+	.align 2
+	.globl oktag_to_io
+	.type	 oktag_to_io,@function
+oktag_to_io:
+	addi 5,5,3
+	srwi 5,5,2
+	cmpwi 1,5,0
+	mr 9,3
+	mr 3,4
+	addi 5,5,-1
+	bc 12,6,.L3
+.L5:
+	cmpwi 1,5,0
+	lwz 0,0(3)
+	addi 3,3,4
+	addi 5,5,-1
+exp1:	stw 0,0(9)
+	bc 4,6,.L5
+.L3:
+ret1:	subf 3,4,3
+	srawi 3,3,2
+	blr
+.Lfe1:
+	.size	 oktag_to_io,.Lfe1-oktag_to_io
+	.align 2
+	.globl oktag_from_io
+	.type	 oktag_from_io,@function
+oktag_from_io:
+	addi 5,5,3
+	srwi 5,5,2
+	cmpwi 1,5,0
+	mr 9,3
+	addi 5,5,-1
+	bc 12,6,.L9
+.L11:
+	cmpwi 1,5,0
+exp2:	lwz 0,0(4)
+	addi 5,5,-1
+	stw 0,0(3)
+	addi 3,3,4
+	bc 4,6,.L11
+.L9:
+ret2:	subf 3,9,3
+	srawi 3,3,2
+	blr
+.Lfe2:
+	.size	 oktag_from_io,.Lfe2-oktag_from_io
+	.ident	"GCC: (GNU) egcs-2.90.29 980515 (egcs-1.0.3 release)"
+
+/*
+ * Exception table.
+ * Second longword shows where to jump when an exception at the addr the first
+ * longword is pointing to is caught.
+ */
+
+.section __ex_table,"a"
+	.align	2
+oktagon_except:
+	.long	exp1,ret1
+	.long	exp2,ret2
+
+#else
+
+/*
+The code which follows is for 680x0 based assembler and is meant for
+Linux/m68k. It was created by cross compiling the code using the
+instructions given above. I then added the four labels used in the
+exception handler table at the bottom of this file.
+- Kevin <kcozens@interlog.com>
+*/
+
+#ifdef CONFIG_AMIGA
+
+	.file	"oktagon_io.c"
+	.version	"01.01"
+gcc2_compiled.:
+.text
+	.align 	2
+.globl oktag_to_io
+	.type	 oktag_to_io,@function
+oktag_to_io:
+	link.w %a6,#0
+	move.l %d2,-(%sp)
+	move.l 8(%a6),%a1
+	move.l 12(%a6),%d1
+	move.l %d1,%a0
+	move.l 16(%a6),%d0
+	addq.l #3,%d0
+	lsr.l #2,%d0
+	subq.l #1,%d0
+	moveq.l #-1,%d2
+	cmp.l %d0,%d2
+	jbeq .L3
+.L5:
+exp1:
+	move.l (%a0)+,(%a1)
+	dbra %d0,.L5
+	clr.w %d0
+	subq.l #1,%d0
+	jbcc .L5
+.L3:
+ret1:
+	move.l %a0,%d0
+	sub.l %d1,%d0
+	asr.l #2,%d0
+	move.l -4(%a6),%d2
+	unlk %a6
+	rts
+
+.Lfe1:
+	.size	 oktag_to_io,.Lfe1-oktag_to_io
+	.align 	2
+.globl oktag_from_io
+	.type	 oktag_from_io,@function
+oktag_from_io:
+	link.w %a6,#0
+	move.l %d2,-(%sp)
+	move.l 8(%a6),%d1
+	move.l 12(%a6),%a1
+	move.l %d1,%a0
+	move.l 16(%a6),%d0
+	addq.l #3,%d0
+	lsr.l #2,%d0
+	subq.l #1,%d0
+	moveq.l #-1,%d2
+	cmp.l %d0,%d2
+	jbeq .L9
+.L11:
+exp2:
+	move.l (%a1),(%a0)+
+	dbra %d0,.L11
+	clr.w %d0
+	subq.l #1,%d0
+	jbcc .L11
+.L9:
+ret2:
+	move.l %a0,%d0
+	sub.l %d1,%d0
+	asr.l #2,%d0
+	move.l -4(%a6),%d2
+	unlk %a6
+	rts
+.Lfe2:
+	.size	 oktag_from_io,.Lfe2-oktag_from_io
+	.ident	"GCC: (GNU) 2.7.2.1"
+
+/*
+ * Exception table.
+ * Second longword shows where to jump when an exception at the addr the first
+ * longword is pointing to is caught.
+ */
+
+.section __ex_table,"a"
+	.align	2
+oktagon_except:
+	.long	exp1,ret1
+	.long	exp2,ret2
+
+#endif
+#endif
diff -urN linux-2.2.18-pre15/drivers/scsi/sun3x_esp.c linux/drivers/scsi/sun3x_esp.c
--- linux-2.2.18-pre15/drivers/scsi/sun3x_esp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/sun3x_esp.c	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,290 @@
+/* sun3x_esp.c:  EnhancedScsiProcessor Sun3x SCSI driver code.
+ *
+ * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * Based on David S. Miller's esp driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "NCR53C9x.h"
+
+#include "sun3x_esp.h"
+#include <asm/sun3x.h>
+#include <asm/dvma.h>
+
+extern struct NCR_ESP *espchain;
+
+static void dma_barrier(struct NCR_ESP *esp);
+static int  dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int  dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_drain(struct NCR_ESP *esp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int  dma_irq_p(struct NCR_ESP *esp);
+static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr);
+static int  dma_ports_p(struct NCR_ESP *esp);
+static void dma_reset(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_advance_sg (Scsi_Cmnd *sp);
+
+volatile unsigned char cmd_buffer[16];
+                                /* This is where all commands are put
+                                 * before they are trasfered to the ESP chip
+                                 * via PIO.
+                                 */
+
+/* Detecting ESP chips on the machine.  This is the simple and easy
+ * version.
+ */
+int sun3x_esp_detect(Scsi_Host_Template *tpnt)
+{
+	struct NCR_ESP *esp;
+	struct ConfigDev *esp_dev;
+
+	esp_dev = 0;
+	esp = esp_allocate(tpnt, (void *) esp_dev);
+
+	/* Do command transfer with DMA */
+	esp->do_pio_cmds = 0;
+
+	/* Required functions */
+	esp->dma_bytes_sent = &dma_bytes_sent;
+	esp->dma_can_transfer = &dma_can_transfer;
+	esp->dma_dump_state = &dma_dump_state;
+	esp->dma_init_read = &dma_init_read;
+	esp->dma_init_write = &dma_init_write;
+	esp->dma_ints_off = &dma_ints_off;
+	esp->dma_ints_on = &dma_ints_on;
+	esp->dma_irq_p = &dma_irq_p;
+	esp->dma_ports_p = &dma_ports_p;
+	esp->dma_setup = &dma_setup;
+
+	/* Optional functions */
+	esp->dma_barrier = &dma_barrier;
+	esp->dma_drain = &dma_drain;
+	esp->dma_irq_entry = &dma_ints_off;
+	esp->dma_irq_exit = &dma_ints_on;
+	esp->dma_led_on = 0;
+	esp->dma_led_off = 0;
+	esp->dma_poll = &dma_poll;
+	esp->dma_reset = &dma_reset;
+
+        /* virtual DMA functions */
+        esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+        esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+        esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
+        esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
+        esp->dma_advance_sg = &dma_advance_sg;
+	    
+	/* SCSI chip speed */
+	esp->cfreq = 20000000;
+	esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE);
+	esp->dregs = (void *)SUN3X_ESP_DMA;
+
+	esp->esp_command = (volatile unsigned char *)cmd_buffer;
+	esp->esp_command_dvma = dvma_alloc(virt_to_phys(cmd_buffer), 
+					   sizeof (cmd_buffer));
+
+	esp->irq = 2;
+	request_irq(esp->irq, esp_intr, SA_INTERRUPT, "SUN3X SCSI", NULL);
+
+	esp->scsi_id = 7;
+	esp->diff = 0;
+
+	esp_initialize(esp);
+
+	printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,
+	       esps_in_use);
+	esps_running = esps_in_use;
+	return esps_in_use;
+}
+
+static void dma_barrier(struct NCR_ESP *esp)
+{
+	struct sparc_dma_registers *dregs =
+		(struct sparc_dma_registers *) esp->dregs;
+
+	while(dregs->cond_reg & DMA_PEND_READ)
+		udelay(1);
+	dregs->cond_reg &= ~(DMA_ENABLE);
+}
+
+/* This uses various DMA csr fields and the fifo flags count value to
+ * determine how many bytes were successfully sent/received by the ESP.
+ */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+	struct sparc_dma_registers *dregs = 
+		(struct sparc_dma_registers *) esp->dregs;
+
+	int rval = dregs->st_addr - esp->esp_command_dvma;
+
+	return rval - fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+	__u32 base, end, sz;
+
+	base = ((__u32)sp->SCp.ptr);
+	base &= (0x1000000 - 1);
+	end = (base + sp->SCp.this_residual);
+	if(end > 0x1000000)
+		end = 0x1000000;
+	sz = (end - base);
+	return sz;
+}
+
+static void dma_drain(struct NCR_ESP *esp)
+{
+	struct sparc_dma_registers *dregs =
+		(struct sparc_dma_registers *) esp->dregs;
+
+	if(dregs->cond_reg & DMA_FIFO_ISDRAIN) {
+		dregs->cond_reg |= DMA_FIFO_STDRAIN;
+		while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
+			udelay(1);
+	}
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+	struct sparc_dma_registers *dregs =
+		(struct sparc_dma_registers *) esp->dregs;
+
+	ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%p>\n",
+		esp->esp_id, dregs->cond_reg, dregs->st_addr));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+	struct sparc_dma_registers *dregs = 
+		(struct sparc_dma_registers *) esp->dregs;
+
+	dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE);
+	dregs->st_addr = vaddress;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+	struct sparc_dma_registers *dregs = 
+		(struct sparc_dma_registers *) esp->dregs;
+
+	/* Set up the DMA counters */
+	dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE);
+	dregs->st_addr = vaddress;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+	DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+	DMA_INTSON((struct sparc_dma_registers *) esp->dregs);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+	return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs);
+}
+
+static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr)
+{
+	dma_drain(esp);
+
+	/* Wait till the first bits settle. */
+	while(vaddr[0] == 0xff)
+		udelay(1);
+}	
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+	return (((struct sparc_dma_registers *) esp->dregs)->cond_reg 
+			& DMA_INT_ENAB);
+}
+
+/* Resetting various pieces of the ESP scsi driver chipset/buses. */
+static void dma_reset(struct NCR_ESP *esp)
+{
+	struct sparc_dma_registers *dregs =
+		(struct sparc_dma_registers *)esp->dregs;
+
+	/* Punt the DVMA into a known state. */
+	dregs->cond_reg |= DMA_RST_SCSI;
+	dregs->cond_reg &= ~(DMA_RST_SCSI);
+	DMA_INTSON(dregs);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+	struct sparc_dma_registers *dregs = 
+		(struct sparc_dma_registers *) esp->dregs;
+	unsigned long nreg = dregs->cond_reg;
+
+	if(write)
+		nreg |= DMA_ST_WRITE;
+	else
+		nreg &= ~(DMA_ST_WRITE);
+	nreg |= DMA_ENABLE;
+	dregs->cond_reg = nreg;
+	dregs->st_addr = addr;
+}
+
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+    sp->SCp.have_data_in = dvma_alloc(virt_to_phys(sp->SCp.buffer),
+				       sp->SCp.this_residual);
+    sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in);
+}
+
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+    int sz = sp->SCp.buffers_residual;
+    struct mmu_sglist *sg = (struct mmu_sglist *) sp->SCp.buffer;
+
+    while (sz >= 0) {
+        sg[sz].dvma_addr = dvma_alloc(virt_to_phys(sg[sz].addr), sg[sz].len);
+        sz--;
+    }
+    sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address);
+}
+
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+    dvma_free(sp->SCp.have_data_in, sp->request_bufflen);
+}
+
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+    int sz = sp->use_sg - 1;
+    struct mmu_sglist *sg = (struct mmu_sglist *)sp->buffer;
+                        
+    while(sz >= 0) {
+        dvma_free(sg[sz].dvma_addr,sg[sz].len);
+        sz--;
+    }
+}
+
+static void dma_advance_sg (Scsi_Cmnd *sp)
+{
+    sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address);
+}
diff -urN linux-2.2.18-pre15/drivers/scsi/sun3x_esp.h linux/drivers/scsi/sun3x_esp.h
--- linux-2.2.18-pre15/drivers/scsi/sun3x_esp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/sun3x_esp.h	Mon Dec  3 20:34:11 2001
@@ -0,0 +1,41 @@
+/* sun3x_esp.h: Defines and structures for the Sun3x ESP
+ *
+ * (C) 1995 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+
+#ifndef _SUN3X_ESP_H
+#define _SUN3X_ESP_H
+
+/* For dvma controller register definitions. */
+#include <asm/dvma.h>
+
+extern int sun3x_esp_detect(struct SHT *);
+extern const char *esp_info(struct Scsi_Host *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_command(Scsi_Cmnd *);
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+extern int esp_proc_info(char *buffer, char **start, off_t offset, int length,
+			 int hostno, int inout);
+
+extern struct proc_dir_entry proc_scsi_esp;
+
+#define DMA_PORTS_P        (dregs->cond_reg & DMA_INT_ENAB)
+
+#define SCSI_SUN3X_ESP {                                        \
+		proc_dir:       &proc_scsi_esp,			\
+		proc_info:      &esp_proc_info,			\
+		name:           "Sun ESP 100/100a/200",		\
+		detect:         sun3x_esp_detect,		\
+		info:           esp_info,			\
+		command:        esp_command,			\
+		queuecommand:   esp_queue,			\
+		abort:          esp_abort,			\
+		reset:          esp_reset,			\
+		can_queue:      7,				\
+		this_id:        7,				\
+		sg_tablesize:   SG_ALL,				\
+		cmd_per_lun:    1,				\
+		use_clustering: DISABLE_CLUSTERING, }
+
+#endif /* !(_SUN3X_ESP_H) */
diff -urN linux-2.2.18-pre15/drivers/scsi/sym53c8xx_defs.h linux/drivers/scsi/sym53c8xx_defs.h
--- linux-2.2.18-pre15/drivers/scsi/sym53c8xx_defs.h	Mon Dec  3 20:33:30 2001
+++ linux/drivers/scsi/sym53c8xx_defs.h	Mon Dec  3 20:34:11 2001
@@ -822,11 +822,11 @@
  FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
  FE_RAM|FE_IO256}							\
  ,									\
- {PCI_DEVICE_ID_LSI_53C1010, 0xff, "1010",  6, 62, 7,			\
+ {PCI_DEVICE_ID_LSI_53C1010, 0xff, "1010",  6, 31, 7,			\
  FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|		\
  FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3}		\
  ,									\
- {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010_66",  6, 62, 7,		\
+ {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010_66",  6, 31, 7,		\
  FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|		\
  FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3|FE_66MHZ}	\
 }
diff -urN linux-2.2.18-pre15/drivers/scsi/wd33c93.c linux/drivers/scsi/wd33c93.c
--- linux-2.2.18-pre15/drivers/scsi/wd33c93.c	Mon Apr 12 16:18:26 1999
+++ linux/drivers/scsi/wd33c93.c	Mon Dec  3 20:34:11 2001
@@ -176,6 +176,7 @@
 static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num)
 {
    regp->SASR = reg_num;
+   mb();
    return(regp->SCMD);
 }
 
@@ -186,14 +187,18 @@
 static inline void write_wd33c93(wd33c93_regs *regp,uchar reg_num, uchar value)
 {
    regp->SASR = reg_num;
+   mb();
    regp->SCMD = value;
+   mb();
 }
 
 
 static inline void write_wd33c93_cmd(wd33c93_regs *regp, uchar cmd)
 {
    regp->SASR = WD_COMMAND;
+   mb();
    regp->SCMD = cmd;
+   mb();
 }
 
 
@@ -216,9 +221,11 @@
 static void write_wd33c93_count(wd33c93_regs *regp,unsigned long value)
 {
    regp->SASR = WD_TRANSFER_COUNT_MSB;
+   mb();
    regp->SCMD = value >> 16;
    regp->SCMD = value >> 8;
    regp->SCMD = value;
+   mb();
 }
 
 
@@ -227,9 +234,11 @@
 unsigned long value;
 
    regp->SASR = WD_TRANSFER_COUNT_MSB;
+   mb();
    value = regp->SCMD << 16;
    value |= regp->SCMD << 8;
    value |= regp->SCMD;
+   mb();
    return value;
 }
 
diff -urN linux-2.2.18-pre15/drivers/scsi/wd33c93.h linux/drivers/scsi/wd33c93.h
--- linux-2.2.18-pre15/drivers/scsi/wd33c93.h	Wed Aug 25 17:29:48 1999
+++ linux/drivers/scsi/wd33c93.h	Mon Dec  3 20:34:11 2001
@@ -227,13 +227,13 @@
     uchar            clock_freq;
     uchar            chip;             /* what kind of wd33c93? */
     uchar            microcode;        /* microcode rev */
+    uchar            dma_buffer_pool;  /* FEF: buffer from chip_ram? */
     int              dma_dir;          /* data transfer dir. */
     dma_setup_t      dma_setup;
     dma_stop_t       dma_stop;
     unsigned int     dma_xfer_mask;
     uchar            *dma_bounce_buffer;
     unsigned int     dma_bounce_len;
-    uchar            dma_buffer_pool;  /* FEF: buffer from chip_ram? */
     volatile uchar   busy[8];          /* index = target, bit = lun */
     volatile Scsi_Cmnd *input_Q;       /* commands waiting to be started */
     volatile Scsi_Cmnd *selecting;     /* trying to select this command */
diff -urN linux-2.2.18-pre15/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
--- linux-2.2.18-pre15/drivers/sound/cs46xx.c	Mon Dec  3 20:33:35 2001
+++ linux/drivers/sound/cs46xx.c	Mon Dec  3 20:34:11 2001
@@ -22,6 +22,9 @@
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
+ *	Changes:
+ *	20000909	Changed cs_read, cs_write and drain_dac
+ *			Nils Faerber <nils@kernelconcepts.de>
  */
  
 #include <linux/module.h>
@@ -777,7 +780,7 @@
 
 		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
 		tmo >>= sample_shift[dmabuf->fmt];
-		tmo += (4096*HZ)/dmabuf->rate;
+		tmo += (2048*HZ)/dmabuf->rate;
 		
 		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
 			printk(KERN_ERR "cs461x: drain_dac, dma timeout? %d\n", count);
@@ -921,6 +924,7 @@
 {
 	struct cs_state *state = (struct cs_state *)file->private_data;
 	struct dmabuf *dmabuf = &state->dmabuf;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -940,6 +944,7 @@
 		return -EFAULT;
 	ret = 0;
 
+	add_wait_queue(&state->dmabuf.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&state->card->lock, flags);
 		if (dmabuf->count > (signed) dmabuf->dmasize) {
@@ -952,6 +957,8 @@
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count < cnt)
 			cnt = dmabuf->count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 		if (cnt > count)
@@ -963,38 +970,20 @@
 			start_adc(state);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
-				return ret;
-			}
-			/* This isnt strictly right for the 810  but it'll do */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
-			/* There are two situations when sleep_on_timeout returns, one is when
-			   the interrupt is serviced correctly and the process is waked up by
-			   ISR ON TIME. Another is when timeout is expired, which means that
-			   either interrupt is NOT serviced correctly (pending interrupt) or it
-			   is TOO LATE for the process to be scheduled to run (scheduler latency)
-			   which results in a (potential) buffer overrun. And worse, there is
-			   NOTHING we can do to prevent it. */
-			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
-#ifdef DEBUG
-				printk(KERN_ERR "cs461x: recording schedule timeout, "
-				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
-				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
-				       dmabuf->hwptr, dmabuf->swptr);
-#endif
-				/* a buffer overrun, we delay the recovery untill next time the
-				   while loop begin and we REALLY have space to record */
+				remove_wait_queue(&state->dmabuf.wait, &wait);
+				break;
 			}
+			schedule();
 			if (signal_pending(current)) {
 				ret = ret ? ret : -ERESTARTSYS;
-				return ret;
+				break;
 			}
 			continue;
 		}
 
 		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
 			if (!ret) ret = -EFAULT;
-			return ret;
+			break;
 		}
 
 		swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -1009,6 +998,8 @@
 		ret += cnt;
 		start_adc(state);
 	}
+	remove_wait_queue(&state->dmabuf.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -1017,8 +1008,9 @@
 static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct cs_state *state = (struct cs_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	struct dmabuf *dmabuf = &state->dmabuf;
-	ssize_t ret;
+	ssize_t ret = 0;
 	unsigned long flags;
 	unsigned swptr;
 	int cnt;
@@ -1035,8 +1027,7 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
-	ret = 0;
-
+	add_wait_queue(&state->dmabuf.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&state->card->lock, flags);
 		if (dmabuf->count < 0) {
@@ -1049,6 +1040,8 @@
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count + cnt > dmabuf->dmasize)
 			cnt = dmabuf->dmasize - dmabuf->count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 		if (cnt > count)
@@ -1060,37 +1053,18 @@
 			start_dac(state);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
-				return ret;
-			}
-			/* Not strictly correct but works */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
-			/* There are two situations when sleep_on_timeout returns, one is when
-			   the interrupt is serviced correctly and the process is waked up by
-			   ISR ON TIME. Another is when timeout is expired, which means that
-			   either interrupt is NOT serviced correctly (pending interrupt) or it
-			   is TOO LATE for the process to be scheduled to run (scheduler latency)
-			   which results in a (potential) buffer underrun. And worse, there is
-			   NOTHING we can do to prevent it. */
-			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
-#ifdef DEBUG
-				printk(KERN_ERR "cs461x: playback schedule timeout, "
-				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
-				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
-				       dmabuf->hwptr, dmabuf->swptr);
-#endif
-				/* a buffer underrun, we delay the recovery untill next time the
-				   while loop begin and we REALLY have data to play */
+				break;
 			}
+			schedule();
 			if (signal_pending(current)) {
 				if (!ret) ret = -ERESTARTSYS;
-				return ret;
+				break;
 			}
 			continue;
 		}
 		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
 			if (!ret) ret = -EFAULT;
-			return ret;
+			break;
 		}
 
 		swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -1106,6 +1080,8 @@
 		ret += cnt;
 		start_dac(state);
 	}
+	remove_wait_queue(&state->dmabuf.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -2524,7 +2500,7 @@
 	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack},
 	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL},
 	{0, 0, "Card without SSID set", NULL, NULL },
-	{0, 0, NULL, NULL}
+	{0, 0, NULL, NULL, NULL}
 };
 
 static int __init cs_install(struct pci_dev *pci_dev)
diff -urN linux-2.2.18-pre15/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c
--- linux-2.2.18-pre15/drivers/sound/pas2_mixer.c	Thu May 14 18:50:50 1998
+++ linux/drivers/sound/pas2_mixer.c	Mon Dec  3 20:34:11 2001
@@ -71,7 +71,7 @@
 
 	if (pas_model == 4)
 	  {
-		  outw(data | (data << 8), (ioaddr ^ translate_code) - 1);
+		  outw(data | (data << 8), (ioaddr + translate_code) - 1);
 		  outb((0x80), 0);
 	} else
 		pas_write(data, ioaddr);
diff -urN linux-2.2.18-pre15/drivers/sound/trident.c linux/drivers/sound/trident.c
--- linux-2.2.18-pre15/drivers/sound/trident.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/sound/trident.c	Mon Dec  3 20:34:11 2001
@@ -115,6 +115,10 @@
 #include <asm/hardirq.h>
 #include <linux/bitops.h>
 
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
+#include <asm/hwrpb.h>
+#endif
+
 #include "dm.h"
 
 #include "trident.h"
@@ -2899,13 +2903,23 @@
 	outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
 
 	if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
-		/* edited by HMSEO for GT sound */
-#ifdef CONFIG_ALPHA_NAUTILUS
-		u16 ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL);
-		trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL,
-				  ac97_data | ALI_EAPD_POWER_DOWN);
-#endif
-		/* edited by HMSEO for GT sound*/
+               /* edited by HMSEO for GT sound
+               09-20-00 changed by Rich Payne (rdp@alphalinux.org) to take into account
+		generic kernels running on Nautilus */
+
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
+		u16 ac97_data;
+		extern struct hwrpb_struct *hwrpb;
+
+		if ((hwrpb->sys_type) == 201) {
+			printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n");
+                	ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL);
+                	trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL,
+                                  ac97_data | ALI_EAPD_POWER_DOWN);
+       		}
+#endif                
+	/* edited by HMSEO for GT sound*/
+
 	}
 
 	/* Enable Address Engine Interrupts */
diff -urN linux-2.2.18-pre15/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.2.18-pre15/drivers/usb/Config.in	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/Config.in	Mon Dec  3 20:34:12 2001
@@ -10,6 +10,7 @@
 
 comment 'Miscellaneous USB options'
    bool '  Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
+   bool '  Support for hot-pluggable USB devices' CONFIG_HOTPLUG
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       bool '  Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH
    else
diff -urN linux-2.2.18-pre15/drivers/usb/audio.c linux/drivers/usb/audio.c
--- linux-2.2.18-pre15/drivers/usb/audio.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/audio.c	Mon Dec  3 20:34:12 2001
@@ -1005,8 +1005,10 @@
 		}
 		spin_lock_irqsave(&as->lock, flags);
 	}
-	if (u->dma.count >= u->dma.dmasize && !u->dma.mapped)
+	if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) {
+		spin_unlock_irqrestore(&as->lock, flags);
 		return 0;
+	}
 	u->flags |= FLG_RUNNING;
 	if (!(u->flags & FLG_URB0RUNNING)) {
 		urb = &u->durb[0].urb;
@@ -1366,8 +1368,10 @@
 		}
 		spin_lock_irqsave(&as->lock, flags);
 	}
-	if (u->dma.count <= 0 && !u->dma.mapped)
+	if (u->dma.count <= 0 && !u->dma.mapped) {
+		spin_unlock_irqrestore(&as->lock, flags);
 		return 0;
+	}
        	u->flags |= FLG_RUNNING;
 	if (!(u->flags & FLG_URB0RUNNING)) {
 		urb = &u->durb[0].urb;
diff -urN linux-2.2.18-pre15/drivers/usb/bluetooth.c linux/drivers/usb/bluetooth.c
--- linux-2.2.18-pre15/drivers/usb/bluetooth.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/bluetooth.c	Mon Dec  3 20:34:12 2001
@@ -1,11 +1,16 @@
 /*
- * bluetooth.c   Version 0.5
+ * bluetooth.c   Version 0.6
  *
  * Copyright (c) 2000 Greg Kroah-Hartman	<greg@kroah.com>
  * Copyright (c) 2000 Mark Douglas Corner	<mcorner@umich.edu>
  *
  * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B
  *
+ * (10/05/2000) Version 0.6 gkh
+ *	Fixed bug with urb->dev not being set properly, now that the usb
+ *	core needs it.
+ *	Got a real major id number and name.
+ *
  * (08/06/2000) Version 0.5 gkh
  *	Fixed problem of not resubmitting the bulk read urb if there is
  *	an error in the callback.  Ericsson devices seem to need this.
@@ -90,8 +95,8 @@
 #define BLUETOOTH_PROGRAMMING_PROTOCOL_CODE	0x01
 
 
-#define BLUETOOTH_TTY_MAJOR	240	/* Prototype number for now */
-#define BLUETOOTH_TTY_MINORS	8
+#define BLUETOOTH_TTY_MAJOR	216	/* real device node major id */
+#define BLUETOOTH_TTY_MINORS	256	/* whole lotta bluetooth devices */
 
 #define USB_BLUETOOTH_MAGIC	0x6d02	/* magic number for bluetooth struct */
 
@@ -139,11 +144,16 @@
 
 	unsigned char *		interrupt_in_buffer;
 	struct urb *		interrupt_in_urb;
+	__u8			interrupt_in_endpointAddress;
+	__u8			interrupt_in_interval;
+	int			interrupt_in_buffer_size;
 
 	unsigned char *		bulk_in_buffer;
 	struct urb *		read_urb;
+	__u8			bulk_in_endpointAddress;
+	int			bulk_in_buffer_size;
 
-	int			bulk_out_size;
+	int			bulk_out_buffer_size;
 	struct urb *		write_urb_pool[NUM_BULK_URBS];
 	__u8			bulk_out_endpointAddress;
 
@@ -313,10 +323,18 @@
 
 #ifndef BTBUGGYHARDWARE
 	/* Start reading from the device */
+	FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, 
+		      usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress),
+		      bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, 
+		      bluetooth_read_bulk_callback, bluetooth);
 	result = usb_submit_urb(bluetooth->read_urb);
 	if (result)
 		dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed with status %d", result);
 #endif
+	FILL_INT_URB(bluetooth->interrupt_in_urb, bluetooth->dev, 
+		     usb_rcvintpipe(bluetooth->dev, bluetooth->interrupt_in_endpointAddress),
+		     bluetooth->interrupt_in_buffer, bluetooth->interrupt_in_buffer_size, 
+		     bluetooth_int_callback, bluetooth, bluetooth->interrupt_in_interval);
 	result = usb_submit_urb(bluetooth->interrupt_in_urb);
 	if (result)
 		dbg(__FUNCTION__ " - usb_submit_urb(interrupt in) failed with status %d", result);
@@ -446,7 +464,7 @@
 					urb->transfer_buffer = NULL;
 				}
 
-				buffer_size = MIN (count, bluetooth->bulk_out_size);
+				buffer_size = MIN (count, bluetooth->bulk_out_buffer_size);
 				
 				new_buffer = kmalloc (buffer_size, GFP_KERNEL);
 				if (new_buffer == NULL) {
@@ -509,7 +527,7 @@
 
 	for (i = 0; i < NUM_BULK_URBS; ++i) {
 		if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
-			room += bluetooth->bulk_out_size;
+			room += bluetooth->bulk_out_buffer_size;
 		}
 	}
 
@@ -628,6 +646,7 @@
 #ifdef BTBUGGYHARDWARE
 void btusb_enable_bulk_read(struct tty_struct *tty){
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	int result;
 
 	if (!bluetooth) {
 		return;
@@ -640,9 +659,15 @@
 		return;
 	}
 
-	if (bluetooth->read_urb)
-		if (usb_submit_urb(bluetooth->read_urb))
-			dbg (__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+	if (bluetooth->read_urb) {
+		FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, 
+			      usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress),
+			      bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, 
+			      bluetooth_read_bulk_callback, bluetooth);
+		result = usb_submit_urb(bluetooth->read_urb);
+		if (result)
+			err (__FUNCTION__ " - failed submitting read urb, error %d", result);
+	}
 }
 
 void btusb_disable_bulk_read(struct tty_struct *tty){
@@ -783,14 +808,20 @@
 	unsigned char *data = urb->transfer_buffer;
 	unsigned int count = urb->actual_length;
 	unsigned int i;
-	uint packet_size;
+	unsigned int packet_size;
+	int result;
 
 #ifdef BTBUGGYHARDWARE
 	if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00)
 	    && (data[2] == 0x00) && (data[3] == 0x00)) {
 		urb->actual_length = 0;
-		if (usb_submit_urb(urb))
-			dbg(__FUNCTION__ " - failed resubmitting read urb");
+		FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, 
+			      usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress),
+			      bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, 
+			      bluetooth_read_bulk_callback, bluetooth);
+		result = usb_submit_urb(bluetooth->read_urb);
+		if (result)
+			err (__FUNCTION__ " - failed resubmitting read urb, error %d", result);
 
 		return;
 	}
@@ -864,8 +895,13 @@
 	}	
 
 exit:
-	if (usb_submit_urb(urb))
-		dbg(__FUNCTION__ " - failed resubmitting read urb");
+	FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, 
+		      usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress),
+		      bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, 
+		      bluetooth_read_bulk_callback, bluetooth);
+	result = usb_submit_urb(bluetooth->read_urb);
+	if (result)
+		err (__FUNCTION__ " - failed resubmitting read urb, error %d", result);
 
 	return;
 }
@@ -1028,7 +1064,8 @@
 		err("No free urbs available");
 		goto probe_error;
 	}
-	buffer_size = endpoint->wMaxPacketSize;
+	bluetooth->bulk_in_buffer_size = buffer_size = endpoint->wMaxPacketSize;
+	bluetooth->bulk_in_endpointAddress = endpoint->bEndpointAddress;
 	bluetooth->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
 	if (!bluetooth->bulk_in_buffer) {
 		err("Couldn't allocate bulk_in_buffer");
@@ -1051,7 +1088,7 @@
 		bluetooth->write_urb_pool[i] = urb;
 	}
 	
-	bluetooth->bulk_out_size = endpoint->wMaxPacketSize * 2;
+	bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2;
 
 	endpoint = interrupt_in_endpoint[0];
 	bluetooth->interrupt_in_urb = usb_alloc_urb(0);
@@ -1059,7 +1096,9 @@
 		err("No free urbs available");
 		goto probe_error;
 	}
-	buffer_size = endpoint->wMaxPacketSize;
+	bluetooth->interrupt_in_buffer_size = buffer_size = endpoint->wMaxPacketSize;
+	bluetooth->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
+	bluetooth->interrupt_in_interval = endpoint->bInterval;
 	bluetooth->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
 	if (!bluetooth->interrupt_in_buffer) {
 		err("Couldn't allocate interrupt_in_buffer");
@@ -1071,7 +1110,7 @@
 
 	/* initialize the devfs nodes for this device and let the user know what bluetooths we are bound to */
 	tty_register_devfs (&bluetooth_tty_driver, 0, minor);
-	info("Bluetooth converter now attached to ttyBLUE%d (or usb/ttblue/%d for devfs)", minor, minor);
+	info("Bluetooth converter now attached to ttyUB%d (or usb/ttub/%d for devfs)", minor, minor);
 
 	bluetooth_table[minor] = bluetooth;
 
@@ -1146,7 +1185,7 @@
 			}
 		}
 		
-		info("Bluetooth converter now disconnected from ttyBLUE%d", bluetooth->minor);
+		info("Bluetooth converter now disconnected from ttyUB%d", bluetooth->minor);
 
 		bluetooth_table[bluetooth->minor] = NULL;
 
@@ -1164,7 +1203,7 @@
 static struct tty_driver bluetooth_tty_driver = {
 	magic:			TTY_DRIVER_MAGIC,
 	driver_name:		"usb-bluetooth",
-	name:			"usb/ttblue/%d",
+	name:			"usb/ttub/%d",
 	major:			BLUETOOTH_TTY_MAJOR,
 	minor_start:		0,
 	num:			BLUETOOTH_TTY_MINORS,
diff -urN linux-2.2.18-pre15/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
--- linux-2.2.18-pre15/drivers/usb/pegasus.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/pegasus.c	Mon Dec  3 20:34:12 2001
@@ -1,11 +1,43 @@
 /*
 **	Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
 **
-**	Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**	Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg)
+**	
 **
-**	Distribute under GPL version 2 or later.
+**	ChangeLog:
+**		....	Most of the time spend reading sources & docs.
+**		v0.2.x	First official release for the Linux kernel.
+**		v0.3.0	Beutified and structured, some bugs fixed.
+**		v0.3.x	URBifying bulk requests and bugfixing. First relatively
+**			stable release. Still can touch device's registers only
+**			from top-halves.
+**		v0.4.0	Control messages remained unurbified are now URBs.
+**			Now we can touch the HW at any time.
+**		v0.4.9	Control urbs again use process context to wait. Argh...
+**			Some long standing bugs (enable_net_traffic) fixed.
+**			Also nasty trick about resubmiting control urb from
+**			interrupt context used. Please let me know how it
+**			behaves. Pegasus II support added since this version.
+**			TODO: suppressing HCD warnings spewage on disconnect.
 */
 
+/*
+ * 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
+ */
+
+
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
@@ -16,240 +48,573 @@
 #include <linux/usb.h>
 
 
-static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.4.10 2000/09/25 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
+
 
+#define	PEGASUS_USE_INTR
+
+
+#define	PEGASUS_II		0x80000000
+#define	HAS_HOME_PNA		0x40000000
 
 #define	PEGASUS_MTU		1500
 #define	PEGASUS_MAX_MTU		1536
-#define	SROM_WRITE		0x01
-#define	SROM_READ		0x02
-#define	PEGASUS_TX_TIMEOUT	(HZ*5)
-#define	PEGASUS_RESET		1
+
+#define	EPROM_WRITE		0x01
+#define	EPROM_READ		0x02
+#define	EPROM_DONE		0x04
+#define	EPROM_WR_ENABLE		0x10
+#define	EPROM_LOAD		0x20
+
+#define	MII_BMCR		0x00
+#define	MII_BMSR		0x01
+#define	BMSR_MEDIA		0x7808
+#define	MII_ANLPA		0x05
+#define	ANLPA_100TX_FD		0x0100
+#define	ANLPA_100TX_HD		0x0080
+#define	ANLPA_10T_FD		0x0040
+#define	ANLPA_10T_HD		0x0020
+#define	PHY_DONE		0x80
+#define	PHY_READ		0x40
+#define	PHY_WRITE		0x20
+#define	DEFAULT_GPIO_RESET	0x24
+#define	LINKSYS_GPIO_RESET	0x24
+#define	DEFAULT_GPIO_SET	0x26
+
+#define	PEGASUS_PRESENT		0x00000001
+#define	PEGASUS_RUNNING		0x00000002
+#define	PEGASUS_TX_BUSY		0x00000004
+#define	PEGASUS_RX_BUSY		0x00000008
+#define	CTRL_URB_RUNNING	0x00000010
+#define	CTRL_URB_SLEEP		0x00000020
+#define	ETH_REGS_CHANGE		0x40000000
+#define	ETH_REGS_CHANGED	0x80000000
+
+#define	RX_MULTICAST		2
+#define	RX_PROMISCUOUS		4
+
+#define	REG_TIMEOUT		(HZ)
+#define	PEGASUS_TX_TIMEOUT	(HZ*10)
+
+#ifdef	PEGASUS_USE_INTR
+#	define	INTR_IVAL	0x80
+#else
+#	define	INTR_IVAL	0
+#endif
+
+#define	TX_UNDERRUN		0x80
+#define	EXCESSIVE_COL		0x40
+#define	LATE_COL		0x20
+#define	NO_CARRIER		0x10
+#define	LOSS_CARRIER		0x08
+#define	JABBER_TIMEOUT		0x04
+
+#define	PEGASUS_REQT_READ	0xc0
+#define	PEGASUS_REQT_WRITE	0x40
+#define	PEGASUS_REQ_GET_REGS	0xf0
+#define	PEGASUS_REQ_SET_REGS	0xf1
+#define	PEGASUS_REQ_SET_REG	PEGASUS_REQ_SET_REGS
+#define	NUM_CTRL_URBS		0x10
 #define	ALIGN(x)		x __attribute__((aligned(L1_CACHE_BYTES)))
 
+enum pegasus_registers {
+	EthCtrl0 = 0,
+	EthCtrl1 = 1,
+	EthCtrl2 = 2,
+	EthID = 0x10,
+	Reg1d = 0x1d,
+	EpromOffset = 0x20,
+	EpromData = 0x21,	/* 0x21 low, 0x22 high byte */
+	EpromCtrl = 0x23,
+	PhyAddr = 0x25,
+	PhyData = 0x26, 	/* 0x26 low, 0x27 high byte */
+	PhyCtrl = 0x28,
+	UsbStst = 0x2a,
+	EthTxStat0 = 0x2b,
+	EthTxStat1 = 0x2c,
+	EthRxStat = 0x2d,
+	Reg7b = 0x7b,
+	Gpio0 = 0x7e,
+	Gpio1 = 0x7f,
+	Reg81 = 0x81,
+};
+
 
-struct pegasus {
+typedef struct pegasus {
 	struct usb_device	*usb;
 	struct net_device	*net;
 	struct net_device_stats	stats;
-	int			flags;
-	spinlock_t		pegasus_lock;
-	struct urb		rx_urb, tx_urb, intr_urb;
-	unsigned char		ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
-	unsigned char		ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
+	unsigned		flags;
+	unsigned		features;
+	struct urb		ctrl_urb, rx_urb, tx_urb, intr_urb;
+	devrequest		dr;
+	wait_queue_head_t	ctrl_wait;
+	struct semaphore	ctrl_sem;
+	unsigned char		ALIGN(rx_buff[PEGASUS_MAX_MTU]);
+	unsigned char		ALIGN(tx_buff[PEGASUS_MAX_MTU]);
 	unsigned char		ALIGN(intr_buff[8]);
-};
+	__u8			eth_regs[4];
+	__u8			phy;
+	__u8			gpio_res;
+} pegasus_t;
 
 struct usb_eth_dev {
 	char	*name;
 	__u16	vendor;
 	__u16	device;
-	void	*private;
+	__u32	private; /* LSB is gpio reset value */
 };
 
 
 static int loopback = 0;
+static int mii_mode = 0;
 static int multicast_filter_limit = 32;
 
 
-MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
+MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>");
 MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
 MODULE_PARM(loopback, "i");
+MODULE_PARM(mode, "i");
+MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
+MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0");
 
 
 static struct usb_eth_dev usb_dev_id[] = {
-	{"Billionton USB-100", 0x08dd, 0x0986, NULL},
-	{"Corega FEter USB-TX", 0x7aa, 0x0004, NULL},
-	{"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, NULL},
-	{"D-Link DSB-650TX", 0x2001, 0x4001, NULL},
-	{"D-Link DSB-650TX", 0x2001, 0x4002, NULL},
-	{"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL},
-	{"D-Link DU-10", 0x07b8, 0xabc1, NULL},
-	{"D-Link DU-E100", 0x07b8, 0x4002, NULL},
-	{"Linksys USB100TX", 0x066b, 0x2203, NULL},
-	{"Linksys USB100TX", 0x066b, 0x2204, NULL},
-	{"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL},
-	{"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL},
-	{"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL},
-	{"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL},
-	{"IO DATA USB ET/TX", 0x04bb, 0x0904, NULL},
-	{"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, NULL},
-	{NULL, 0, 0, NULL}
+	{"Billionton USB-100", 0x08dd, 0x0986, DEFAULT_GPIO_RESET},
+	{"Corega FEter USB-TX", 0x7aa, 0x0004, DEFAULT_GPIO_RESET},
+	{"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, DEFAULT_GPIO_RESET},
+	{"D-Link DSB-650TX", 0x2001, 0x4001, LINKSYS_GPIO_RESET},
+	{"D-Link DSB-650TX", 0x2001, 0x4002, LINKSYS_GPIO_RESET},
+	{"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, DEFAULT_GPIO_RESET},
+	{"D-Link DSB-650", 0x2001, 0xabc1, DEFAULT_GPIO_RESET},
+	{"D-Link DU-E10", 0x07b8, 0xabc1, DEFAULT_GPIO_RESET},
+	{"D-Link DU-E100", 0x07b8, 0x4002, DEFAULT_GPIO_RESET},
+	{"Linksys USB10TX", 0x066b, 0x2202, LINKSYS_GPIO_RESET},
+	{"Linksys USB100TX", 0x066b, 0x2203, LINKSYS_GPIO_RESET},
+	{"Linksys USB100TX", 0x066b, 0x2204, LINKSYS_GPIO_RESET},
+	{"Linksys USB Ethernet Adapter", 0x066b, 0x2206, LINKSYS_GPIO_RESET},
+	{"SMC 202 USB Ethernet", 0x0707, 0x0200, DEFAULT_GPIO_RESET},
+	{"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, 
+		HAS_HOME_PNA | DEFAULT_GPIO_RESET},
+	{"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, 
+		DEFAULT_GPIO_RESET},
+	{"IO DATA USB ET/TX", 0x04bb, 0x0904, DEFAULT_GPIO_RESET},
+	{"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, DEFAULT_GPIO_RESET},
+	{"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, DEFAULT_GPIO_RESET},
+	{"ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511, 
+		PEGASUS_II | DEFAULT_GPIO_RESET},
+	{NULL, 0, 0, 0}
 };
 
 
-#define pegasus_get_registers(dev, indx, size, data)\
-	usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
-#define pegasus_set_registers(dev, indx, size, data)\
-	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
-#define pegasus_set_register(dev, indx, value)	\
-	{ __u8	__data = value;			\
-	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);}
+static int update_eth_regs_async( pegasus_t * );
+/* Aargh!!! I _really_ hate such tweaks */
+static void ctrl_callback( urb_t *urb )
+{
+	pegasus_t		*pegasus = urb->context;
+
+	if ( !pegasus )
+		return;
+
+	switch ( urb->status ) {
+		case USB_ST_NOERROR:
+			if ( pegasus->flags & ETH_REGS_CHANGE ) {
+				pegasus->flags &= ~ETH_REGS_CHANGE;
+				pegasus->flags |= ETH_REGS_CHANGED;
+				update_eth_regs_async( pegasus );
+				return;
+			}
+			break;
+		case USB_ST_URB_PENDING:
+			return;
+		case USB_ST_URB_KILLED:
+			break;
+		default:
+			warn( __FUNCTION__ " status %d", urb->status);
+	}
+	pegasus->flags &= ~ETH_REGS_CHANGED;
+	if ( pegasus->flags & CTRL_URB_SLEEP ) {
+		pegasus->flags &= ~CTRL_URB_SLEEP;
+		wake_up_interruptible( &pegasus->ctrl_wait );
+	}
+}
 
 
-static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
+static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
 {
-	int i;
-	__u8 data[4] = { 1, 0, 0, 0x40 + index };
+	int	ret;
 
-	pegasus_set_registers(dev, 0x25, 4, data);
-	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x26, 3, data);
-		if (data[2] & 0x80) {
-			*regdata = *(__u16 *)(data);
-			return 0;
-		}
-		udelay(100);
+	if ( pegasus->flags & ETH_REGS_CHANGED ) {
+		pegasus->flags |= CTRL_URB_SLEEP;
+		interruptible_sleep_on( &pegasus->ctrl_wait );
 	}
+	pegasus->dr.requesttype = PEGASUS_REQT_READ;
+	pegasus->dr.request = PEGASUS_REQ_GET_REGS;
+	pegasus->dr.value = 0;
+	pegasus->dr.index = cpu_to_le16p(&indx);
+	pegasus->dr.length = 
+	pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p(&size);
 
-	warn("read_phy_word() failed");
-	return 1;
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_rcvctrlpipe(pegasus->usb,0),
+			  (char *)&pegasus->dr,
+			  data, size, ctrl_callback, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
+		err( __FUNCTION__ " BAD CTRLs %d", ret);
+		return	ret;
+	}
+	pegasus->flags |= CTRL_URB_SLEEP;
+	interruptible_sleep_on( &pegasus->ctrl_wait );
+
+	return	ret;
 }
 
 
-static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
+static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
 {
-	int i;
-	__u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
+	int	ret;
 
-	pegasus_set_registers(dev, 0x25, 4, data);
-	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x28, 1, data);
-		if (data[0] & 0x80)
-			return 0;
-		udelay(100);
+	if ( pegasus->flags & ETH_REGS_CHANGED ) {
+		pegasus->flags |= CTRL_URB_SLEEP ;
+		interruptible_sleep_on( &pegasus->ctrl_wait );
 	}
+	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+	pegasus->dr.request = PEGASUS_REQ_SET_REGS;
+	pegasus->dr.value = 0;
+	pegasus->dr.index = cpu_to_le16p( &indx );
+	pegasus->dr.length = 
+	pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p( &size );
 
-	warn("write_phy_word() failed");
-	return 1;
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_sndctrlpipe(pegasus->usb,0),
+			  (char *)&pegasus->dr,
+			  data, size, ctrl_callback, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
+		err( __FUNCTION__ " BAD CTRL %d", ret);
+		return	ret;
+	}
+	pegasus->flags |= CTRL_URB_SLEEP;
+	interruptible_sleep_on( &pegasus->ctrl_wait );
+
+	return	ret;
 }
 
 
-static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction)
+static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
 {
-	int i;
-	__u8 data[4] = { index, 0, 0, direction };
+	int	ret;
 
-	pegasus_set_registers(dev, 0x20, 4, data);
-	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x23, 1, data);
-		if (data[0] & 4) {
-			pegasus_get_registers(dev, 0x21, 2, data);
-			*retdata = *(__u16 *)data;
-			return 0;
-		}
+	if ( pegasus->flags & ETH_REGS_CHANGED ) {
+		pegasus->flags |= CTRL_URB_SLEEP;
+		interruptible_sleep_on( &pegasus->ctrl_wait );
+	}
+	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+	pegasus->dr.request = PEGASUS_REQ_SET_REG;
+	pegasus->dr.value = data;
+	pegasus->dr.index = cpu_to_le16p( &indx );
+	pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1;
+
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_sndctrlpipe(pegasus->usb,0),
+			  (char *)&pegasus->dr,
+			  &data, 1, ctrl_callback, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
+		err( __FUNCTION__ " BAD CTRL %d", ret);
+		return	ret;
 	}
+	pegasus->flags |= CTRL_URB_SLEEP;
+	interruptible_sleep_on( &pegasus->ctrl_wait );
+	
+	return	ret;
+}
+
+
+static int update_eth_regs_async( pegasus_t *pegasus )
+{
+	int	ret;
+
+	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+	pegasus->dr.request = PEGASUS_REQ_SET_REGS;
+	pegasus->dr.value = 0;
+	pegasus->dr.index = EthCtrl0;
+	pegasus->dr.length = 
+	pegasus->ctrl_urb.transfer_buffer_length = 3;
+
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_sndctrlpipe(pegasus->usb,0),
+			  (char *)&pegasus->dr,
+			  pegasus->eth_regs, 3, ctrl_callback, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) )
+		err( __FUNCTION__ " BAD CTRL %d, flags %x",ret,pegasus->flags );
+
+	return	ret;
+}
+
+
+static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
+{
+	int	i;
+	__u8	data[4] = { phy, 0, 0, indx };
+
+	set_register( pegasus, PhyCtrl, 0 );
+	set_registers( pegasus, PhyAddr, sizeof(data), data );
+	set_register( pegasus, PhyCtrl, (indx | PHY_READ) );
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, PhyCtrl, 1, data);
+		if ( data[0] & PHY_DONE ) 
+			break;
+	}
+	if ( i < REG_TIMEOUT ) {
+		get_registers( pegasus, PhyData, 2, regd );
+		return	0;
+	}
+	warn( __FUNCTION__ " failed" );
+	
+	return 1;
+}
+
+
+static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
+{
+	int	i;
+	__u8	data[4] = { phy, 0, 0, indx };
+	
+	*(data + 1) = cpu_to_le16p( &regd );
+	set_register( pegasus, PhyCtrl, 0 );
+	set_registers( pegasus, PhyAddr, 4, data );
+	set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) );
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, PhyCtrl, 1, data);
+		if ( data[0] & PHY_DONE ) 
+			break;
+	}
+	if ( i < REG_TIMEOUT )
+		return	0;
+	warn( __FUNCTION__ " failed" );
 
-	warn("pegasus_rw_srom_word() failed");
 	return 1;
 }
 
 
-static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
+static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata )
+{
+	int	i, tmp;
+
+	set_register( pegasus, EpromCtrl, 0 );
+	set_register( pegasus, EpromOffset, index );
+	set_register( pegasus, EpromCtrl, EPROM_READ); 
+	for ( i=0; i < REG_TIMEOUT; i++ ) {
+		get_registers( pegasus, EpromCtrl, 1, &tmp );
+		if ( tmp & EPROM_DONE )
+			break;
+	}
+	if ( i < REG_TIMEOUT ) {
+		get_registers( pegasus, EpromData, 2, retdata );
+		return	0;
+	}
+	warn( __FUNCTION__ " failed" );
+
+	return -1;
+}
+
+
+static inline void enable_eprom_write( pegasus_t *pegasus )
+{
+	__u8	tmp;
+
+	get_registers( pegasus, EthCtrl2, 1, &tmp );
+	set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE );
+}
+
+
+static inline void disable_eprom_write( pegasus_t *pegasus )
+{
+	__u8 	tmp;
+
+	get_registers( pegasus, EthCtrl2, 1, &tmp );
+	set_register( pegasus, EpromCtrl, 0 );
+	set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE );
+}
+
+
+static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data )
+{
+	int	i, tmp;
+	__u8	d[4] = {0x3f, 0, 0, EPROM_WRITE};
+
+	set_registers( pegasus, EpromOffset, 4, d );
+	enable_eprom_write( pegasus );
+	set_register( pegasus, EpromOffset, index );
+	set_registers( pegasus, EpromData, 2, &data );
+	set_register( pegasus, EpromCtrl, EPROM_WRITE );
+
+	for ( i=0; i < REG_TIMEOUT; i++ ) {
+		get_registers( pegasus, EpromCtrl, 1, &tmp );
+		if ( tmp & EPROM_DONE )
+			break;
+	}
+	disable_eprom_write( pegasus );
+	if ( i < REG_TIMEOUT )
+		return	0;
+	warn( __FUNCTION__ " failed" );
+	return	-1;
+}
+
+
+static void set_intr_interval( pegasus_t *pegasus )
+{
+	__u16	d;
+	__u8	tmp;
+
+	read_eprom_word( pegasus, 4, &d );
+	((__u8 *)&d)[1] = INTR_IVAL;
+	write_eprom_word( pegasus, 4, d );
+	get_registers( pegasus, EthCtrl2, 1, &tmp );
+	set_register( pegasus, EthCtrl2, tmp | EPROM_LOAD );
+	udelay( 10000 );
+	set_register( pegasus, EthCtrl2, tmp );
+#ifdef	PEGASUS_DUMP_EEPROM  
+	{ int	i;
+	for ( i=0; i < 0x40; i++ ) {
+		read_eprom_word( pegasus, i, &d );
+		printk( "eepromword %02x-%04x, ", i, d );
+	}
+	printk( "\n" );
+	}
+#endif
+}
+
+
+static inline int get_node_id( pegasus_t *pegasus, __u8 *id )
 {
 	int i;
+
 	for (i = 0; i < 3; i++)
-		if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ))
+		if ( read_eprom_word( pegasus, i, (__u16 *)&id[i*2]) )
 			return 1;
 	return 0;
 }
 
 
-static int pegasus_reset_mac(struct usb_device *dev)
+static inline int reset_mac( pegasus_t *pegasus )
 {
-	__u8 data = 0x8;
-	int i;
+	__u8	data = 0x8;
+	int	i;
 
-	pegasus_set_register(dev, 1, data);
-	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 1, 1, &data);
+	set_register(pegasus, EthCtrl1, data);
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, EthCtrl1, 1, &data);
 		if (~data & 0x08) {
 			if (loopback & 1) 
-				return 0;
-			if (loopback & 2) 
-				pegasus_write_phy_word(dev, 0, 0x4000);
-			pegasus_set_register(dev, 0x7e, 0x24);
-			pegasus_set_register(dev, 0x7e, 0x27);
-			return 0;
+				break;
+			if ( mii_mode && (pegasus->features & HAS_HOME_PNA) )
+				set_register( pegasus, Gpio1, 0x34 );
+			else
+				set_register( pegasus, Gpio1, 0x26 );
+			set_register( pegasus, Gpio0, pegasus->features );
+			set_register( pegasus, Gpio0, DEFAULT_GPIO_SET );
+			break;
 		}
 	}
-
-	return 1;
+	if ( i == REG_TIMEOUT )
+		return 1;
+	return	0;
 }
 
 
-static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
+static int enable_net_traffic( struct net_device *dev, struct usb_device *usb )
 {
-	__u16 partmedia, temp;
-	__u8 node_id[6];
-	__u8 data[4];
+	__u16	linkpart, bmsr;
+	__u8	node_id[6];
+	__u8	data[4];
+	pegasus_t *pegasus = dev->priv;
 
-	if (pegasus_get_node_id(usb, node_id)) 
+	if ( get_node_id(pegasus, node_id) ) 
 		return 1;
 
-	pegasus_set_registers(usb, 0x10, 6, node_id);
-	memcpy(dev->dev_addr, node_id, 6);
-	if (pegasus_read_phy_word(usb, 1, &temp)) 
+	set_registers( pegasus, EthID, sizeof(node_id), node_id );
+	memcpy( dev->dev_addr, node_id, sizeof(node_id) );
+	if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) 
 		return 2;
-
-	if ((~temp & 4) && !loopback) {
-		warn("%s: link NOT established (0x%x), check the cable.",
-			dev->name, temp);
-		/* return 3; FIXME */
-	}
-
-	if (pegasus_read_phy_word(usb, 5, &partmedia))
+	if ( !(bmsr & 0x20) && !loopback ) 
+		warn( "%s: link NOT established (0x%x) - check the cable.",
+			dev->name, bmsr );
+	if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )
 		return 4;
-
-	if ((partmedia & 0x1f) != 1) {
-		warn("party FAIL %x", partmedia);
-		/* return 5;	FIXME */ 
-	}
+	if ( !(linkpart & 1) )
+		warn( "link partner stat %x", linkpart );
 
 	data[0] = 0xc9;
-	data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+	data[1] = 0;
+	if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) )
+		data[1] |= 0x20; /* set full duplex */
+	if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) )
+		data[1] |= 0x10; /* set 100 Mbps */
+	if ( mii_mode )
+		data[1] = 0;
 	data[2] = (loopback & 1) ? 0x09 : 0x01;
 
-	pegasus_set_registers(usb, 0, 3, data);
+	*(unsigned *)pegasus->eth_regs = *(unsigned *)data;
+
+	set_registers( pegasus, EthCtrl0, 3, data );
 
 	return 0;
 }
 
 
-static void pegasus_read_bulk(struct urb *urb)
+static void read_bulk_callback( struct urb *urb )
 {
-	struct pegasus *pegasus = urb->context;
-	struct net_device *net = pegasus->net;
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net;
 	int count = urb->actual_length, res;
-	int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+	int rx_status;
 	struct sk_buff	*skb;
 	__u16 pkt_len;
 
+	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+		return;
+
+	net = pegasus->net;
+	if ( !netif_device_present(net) )
+		return;
+
+	if ( pegasus->flags & PEGASUS_RX_BUSY ) {
+		pegasus->stats.rx_errors++;
+		return;
+	}
+	pegasus->flags |= PEGASUS_RX_BUSY;
+
+	rx_status = *(int *)(pegasus->rx_buff + count - 4);
+
 	if (urb->status) {
 		dbg("%s: RX status %d", net->name, urb->status);
 		goto goon;
 	}
 
-	if (!count)
-		goto goon;
-#if 0
-	if (rx_status & 0x00010000)
+	if ( !count )
 		goto goon;
-#endif
-	if (rx_status & 0x000e0000) {
+
+	if ( rx_status & 0x000e0000 ) {
 
 		dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
 		pegasus->stats.rx_errors++;
-		if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
-		if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
-		if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+		if ( rx_status & 0x060000 )
+			pegasus->stats.rx_length_errors++;
+		if ( rx_status & 0x080000 )
+			pegasus->stats.rx_crc_errors++;
+		if ( rx_status & 0x100000 )
+			pegasus->stats.rx_frame_errors++;
 
 		goto goon;
 	}
 
 	pkt_len = (rx_status & 0xfff) - 8;
 
-	if(!(skb = dev_alloc_skb(pkt_len+2)))
+	if ( !(skb = dev_alloc_skb(pkt_len+2)) )
 		goto goon;
 
 	skb->dev = net;
@@ -263,79 +628,103 @@
 	pegasus->stats.rx_bytes += pkt_len;
 
 goon:
-	if ((res = usb_submit_urb(&pegasus->rx_urb)))
-		warn("(prb)failed rx_urb %d", res);
+	FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb,
+			usb_rcvbulkpipe(pegasus->usb, 1),
+			pegasus->rx_buff, PEGASUS_MAX_MTU, 
+			read_bulk_callback, pegasus );
+	if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
+		warn( __FUNCTION__ " failed submint rx_urb %d", res);
+	pegasus->flags &= ~PEGASUS_RX_BUSY;
 }
 
 
-static void pegasus_irq(urb_t *urb)
+static void write_bulk_callback( struct urb *urb )
 {
-	__u8	*d = urb->transfer_buffer;
-	
-	if ( d[0] )
-		dbg("txst0=0x%2x", d[0]);
-}
+	pegasus_t *pegasus = urb->context;
 
+	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+		return;
 
-static void pegasus_write_bulk(struct urb *urb)
-{
-	struct pegasus *pegasus = urb->context;
+	if ( !netif_device_present(pegasus->net) )
+		return;
+		
+	if ( urb->status )
+		info("%s: TX status %d", pegasus->net->name, urb->status);
 
-	spin_lock(&pegasus->pegasus_lock);
+	netif_wake_queue( pegasus->net );
+}
 
-	if (urb->status)
-		info("%s: TX status %d", pegasus->net->name, urb->status);
-	netif_wake_queue(pegasus->net);
 
-	spin_unlock(&pegasus->pegasus_lock);
+static void intr_callback( struct urb *urb )
+{
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net;
+	__u8	*d;
+
+	if ( !pegasus )
+		return;
+	d = urb->transfer_buffer;
+	net = pegasus->net;
+	if ( d[0] & 0xfc ) {
+		pegasus->stats.tx_errors++;
+		if ( d[0] & TX_UNDERRUN )
+			pegasus->stats.tx_fifo_errors++;
+		if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
+			pegasus->stats.tx_aborted_errors++;
+		if ( d[0] & LATE_COL )
+			pegasus->stats.tx_window_errors++;
+		if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
+			pegasus->stats.tx_carrier_errors++;
+	}
+	switch ( urb->status ) {
+		case USB_ST_NOERROR:
+			break;
+		case USB_ST_URB_KILLED:
+			break;
+		default:
+			info("intr status %d", urb->status);
+	}
 }
 
-#if 0
-static void pegasus_tx_timeout(struct net_device *net)
+
+static void pegasus_tx_timeout( struct net_device *net )
 {
-	struct pegasus *pegasus = net->priv;
+	pegasus_t *pegasus = net->priv;
 
-	warn("%s: Tx timed out. Reseting...", net->name);
-	usb_unlink_urb(&pegasus->tx_urb);
+	if ( !pegasus )
+		return;
+	
+	usb_unlink_urb( &pegasus->tx_urb );
+	warn("%s: Tx timed out.", net->name);
 	pegasus->stats.tx_errors++;
 	net->trans_start = jiffies;
-	pegasus->flags |= PEGASUS_RESET;
 
-	netif_wake_queue(net);
+	netif_wake_queue( net );
 }
-#endif
 
 
-static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
+static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net )
 {
-	struct pegasus	*pegasus = net->priv;
-	int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
-	int res;
-
-	if (net->tbusy) {
-	  int tickssofar = jiffies - net->trans_start;
-	  if (tickssofar < PEGASUS_TX_TIMEOUT)
-	    return 1;
-	  warn("%s: Tx timed out. Reseting...", net->name);
-	  pegasus->stats.tx_errors++;
-	  net->trans_start = jiffies;
-	  net->tbusy = 0;
-	  netif_wake_queue(net);
-	  dev_kfree_skb(skb);
-	  return 0;
-	}
-	spin_lock(&pegasus->pegasus_lock);
-
-	netif_stop_queue(net);
+	pegasus_t	*pegasus = net->priv;
+	int 	count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+	int 	res;
 
+	netif_stop_queue( net );
+	if ( !(pegasus->flags & PEGASUS_RUNNING) )
+		return	0;
+		
 	((__u16 *)pegasus->tx_buff)[0] = skb->len;
 	memcpy(pegasus->tx_buff+2, skb->data, skb->len);
-	(&pegasus->tx_urb)->transfer_buffer_length = count;
-
+	FILL_BULK_URB( &pegasus->tx_urb, pegasus->usb,
+			usb_sndbulkpipe(pegasus->usb, 2),
+			pegasus->tx_buff, PEGASUS_MAX_MTU, 
+			write_bulk_callback, pegasus );
+	pegasus->tx_urb.transfer_buffer_length = count;
+	pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK;
 	if ((res = usb_submit_urb(&pegasus->tx_urb))) {
 		warn("failed tx_urb %d", res);
 		pegasus->stats.tx_errors++;
-		netif_start_queue(net);
+		netif_start_queue( net );
 	} else {
 		pegasus->stats.tx_packets++;
 		pegasus->stats.tx_bytes += skb->len;
@@ -344,35 +733,41 @@
 
 	dev_kfree_skb(skb);
 
-	spin_unlock(&pegasus->pegasus_lock);
-
 	return 0;
 }
 
 
-static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
+static struct net_device_stats *pegasus_netdev_stats( struct net_device *dev )
+{
+	return &((pegasus_t *)dev->priv)->stats;
+}
+
+
+static inline void disable_net_traffic( pegasus_t *pegasus )
 {
-	return &((struct pegasus *)dev->priv)->stats;
+	int 	tmp=0;
+
+	set_registers( pegasus, EthCtrl0, 2, &tmp );
 }
 
 
 static int pegasus_open(struct net_device *net)
 {
-	struct pegasus *pegasus = (struct pegasus *)net->priv;
-	int res;
+	pegasus_t *pegasus = (pegasus_t *)net->priv;
+	int	res;
 
-	if ((res = pegasus_start_net(net, pegasus->usb))) {
-		err("can't start_net() - %d", res);
+	if ( (res = enable_net_traffic(net, pegasus->usb)) ) {
+		err("can't enable_net_traffic() - %d", res);
 		return -EIO;
 	}
-
-	if ((res = usb_submit_urb(&pegasus->rx_urb)))
-		warn("(open)failed rx_urb %d", res);
-		
-	if ((res = usb_submit_urb(&pegasus->intr_urb)))
-		warn("(open)failed intr_urb %d", res);
-		
-	netif_start_queue(net);
+	if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
+		warn( __FUNCTION__ " failed rx_urb %d", res );
+#ifdef	PEGASUS_USE_INTR
+	if ( (res = usb_submit_urb(&pegasus->intr_urb)) )
+		warn( __FUNCTION__ " failed intr_urb %d", res);
+#endif		
+	netif_start_queue( net );
+	pegasus->flags |= PEGASUS_RUNNING;
 
 	MOD_INC_USE_COUNT;
 
@@ -380,18 +775,18 @@
 }
 
 
-static int pegasus_close(struct net_device *net)
+static int pegasus_close( struct net_device *net )
 {
-	struct pegasus	*pegasus = net->priv;
+	pegasus_t	*pegasus = net->priv;
 
-	netif_stop_queue(net);
-
-	if ( pegasus->rx_urb.status == -EINPROGRESS )
-		usb_unlink_urb(&pegasus->rx_urb);
-	if ( pegasus->tx_urb.status == -EINPROGRESS )	
-		usb_unlink_urb(&pegasus->tx_urb);
-	if ( pegasus->intr_urb.status == -EINPROGRESS )
-		usb_unlink_urb(&pegasus->intr_urb);
+	netif_stop_queue( net );
+	pegasus->flags &= ~PEGASUS_RUNNING;
+	disable_net_traffic( pegasus );
+
+	usb_unlink_urb( &pegasus->rx_urb );
+	usb_unlink_urb( &pegasus->tx_urb );
+	usb_unlink_urb( &pegasus->ctrl_urb );
+	usb_unlink_urb( &pegasus->intr_urb );
 
 	MOD_DEC_USE_COUNT;
 
@@ -399,21 +794,21 @@
 }
 
 
-static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
 {
 	__u16 *data = (__u16 *)&rq->ifr_data;
-	struct pegasus	*pegasus = net->priv;
+	pegasus_t	*pegasus = net->priv;
 
 	switch(cmd) {
 		case SIOCDEVPRIVATE:
-			data[0] = 1;
+			data[0] = pegasus->phy;
 		case SIOCDEVPRIVATE+1:
-			pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+			read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]);
 			return 0;
 		case SIOCDEVPRIVATE+2:
-			if (!capable(CAP_NET_ADMIN))
+			if ( !capable(CAP_NET_ADMIN) )
 				return -EPERM;
-			pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+			write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
 			return 0;
 		default:
 			return -EOPNOTSUPP;
@@ -421,24 +816,29 @@
 }
 
 
-static void pegasus_set_rx_mode(struct net_device *net)
+static void pegasus_set_multicast( struct net_device *net )
 {
-	struct pegasus *pegasus = net->priv;
+	pegasus_t *pegasus = net->priv;
 
 	netif_stop_queue(net);
 
 	if (net->flags & IFF_PROMISC) {
+		pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
 		info("%s: Promiscuous mode enabled", net->name);
-/*		pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */
 	} else if ((net->mc_count > multicast_filter_limit) ||
 			(net->flags & IFF_ALLMULTI)) {
-		pegasus_set_register(pegasus->usb, 0, 0xfa);
-		pegasus_set_register(pegasus->usb, 2, 0);
+		pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
+		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
 		info("%s set allmulti", net->name);
 	} else {
+		pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
+		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
 		info("%s: set Rx mode", net->name);
 	}
 
+	pegasus->flags |= ETH_REGS_CHANGE;
+	ctrl_callback( &pegasus->ctrl_urb );
+
 	netif_wake_queue(net);
 }
 
@@ -457,10 +857,38 @@
 }
 
 
-static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
+static __u8 mii_phy_probe( pegasus_t *pegasus )
+{
+	int	i;
+	__u16	tmp;
+
+	for ( i=0; i < 32; i++ ) {
+		read_phy_word( pegasus, i, MII_BMSR, &tmp );
+		if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
+			continue;
+		else
+			return	i;
+	}
+
+	return	0;
+}
+
+
+static inline void setup_pegasus_II( pegasus_t *pegasus )
+{
+	set_register( pegasus, Reg1d, 0 );
+	set_register( pegasus, Reg7b, 2 );
+	if ( pegasus->features & HAS_HOME_PNA  && mii_mode )
+		set_register( pegasus, Reg81, 6 );
+	else
+		set_register( pegasus, Reg81, 2 );
+}
+
+
+static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum )
 {
 	struct net_device *net;
-	struct pegasus *pegasus;
+	pegasus_t *pegasus;
 	int	dev_indx;
 
 	if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) {
@@ -478,12 +906,6 @@
 	}
 	memset(pegasus, 0, sizeof(struct pegasus));
 
-	if (pegasus_reset_mac(dev)) {
-		err("can't reset MAC");
-		kfree(pegasus);
-		return NULL;
-	}
-	
 	net = init_etherdev(0, 0);
 	net->priv = pegasus;
 	net->open = pegasus_open;
@@ -494,52 +916,74 @@
 #endif
 	net->do_ioctl = pegasus_ioctl;
 	net->hard_start_xmit = pegasus_start_xmit;
-	net->set_multicast_list = pegasus_set_rx_mode;
+	net->set_multicast_list = pegasus_set_multicast;
 	net->get_stats = pegasus_netdev_stats;
 	net->mtu = PEGASUS_MTU;
 
+	init_MUTEX( &pegasus-> ctrl_sem );
+	init_waitqueue_head( &pegasus->ctrl_wait );
+
 	pegasus->usb = dev;
 	pegasus->net = net;
-	pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
 
-	FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
-			pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, 
-			pegasus);
-	FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
-			pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
-			pegasus);
-	FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
-			pegasus->intr_buff, 8, pegasus_irq, pegasus, 500);
+	FILL_BULK_URB( &pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+			pegasus->rx_buff, PEGASUS_MAX_MTU, 
+			read_bulk_callback, pegasus );
+	FILL_BULK_URB( &pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+			pegasus->tx_buff, PEGASUS_MAX_MTU, 
+			write_bulk_callback, pegasus );
+	FILL_INT_URB( &pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
+			pegasus->intr_buff, 8, intr_callback,
+			pegasus, INTR_IVAL );
 
+	pegasus->features = usb_dev_id[dev_indx].private;
+	if ( reset_mac(pegasus) ) {
+		err("can't reset MAC");
+		unregister_netdev( pegasus->net );
+		kfree(pegasus);
+		pegasus = NULL;
+		return NULL;
+	}
 
-	printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name);
+	if ( pegasus->features & PEGASUS_II ) {
+		info( "setup Pegasus II specific registers" );
+		setup_pegasus_II( pegasus );
+	}
+
+	pegasus->phy = mii_phy_probe( pegasus );
+	if ( !pegasus->phy ) {
+		warn( "can't locate MII phy, using default" );
+		pegasus->phy = 1;
+	}
+
+	set_intr_interval( pegasus );
+
+	info( "%s: %s", net->name, usb_dev_id[dev_indx].name );
 
 	return pegasus;
 }
 
 
-static void pegasus_disconnect(struct usb_device *dev, void *ptr)
+static void pegasus_disconnect( struct usb_device *dev, void *ptr )
 {
 	struct pegasus *pegasus = ptr;
 
-	if (!pegasus) {
+	if ( !pegasus ) {
 		warn("unregistering non-existant device");
 		return;
 	}
 
-	if (pegasus->net->flags & IFF_UP)
-		dev_close(pegasus->net);
-
-	unregister_netdev(pegasus->net);
-
-	if ( pegasus->rx_urb.status == -EINPROGRESS )
-		usb_unlink_urb(&pegasus->rx_urb);
-	if ( pegasus->tx_urb.status == -EINPROGRESS )
-		usb_unlink_urb(&pegasus->tx_urb);
-	if ( pegasus->intr_urb.status == -EINPROGRESS )
-		usb_unlink_urb(&pegasus->intr_urb);
-
-	kfree(pegasus);
+	netif_stop_queue( pegasus->net );
+	if ( pegasus->flags & PEGASUS_RUNNING ) {
+		pegasus->flags &= ~PEGASUS_RUNNING;
+		usb_unlink_urb( &pegasus->rx_urb );
+		usb_unlink_urb( &pegasus->tx_urb );
+		usb_unlink_urb( &pegasus->ctrl_urb );
+		usb_unlink_urb( &pegasus->intr_urb );
+	}
+	unregister_netdev( pegasus->net );
+	kfree( pegasus );
+	pegasus = NULL;
 }
 
 
@@ -551,14 +995,14 @@
 
 int __init pegasus_init(void)
 {
-	printk( version );
-	return usb_register(&pegasus_driver);
+	info( "%s", version );
+	return usb_register( &pegasus_driver );
 }
 
 void __exit pegasus_exit(void)
 {
-	usb_deregister(&pegasus_driver);
+	usb_deregister( &pegasus_driver );
 }
 
-module_init(pegasus_init);
-module_exit(pegasus_exit);
+module_init( pegasus_init );
+module_exit( pegasus_exit );
diff -urN linux-2.2.18-pre15/drivers/usb/plusb.c linux/drivers/usb/plusb.c
--- linux-2.2.18-pre15/drivers/usb/plusb.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/plusb.c	Mon Dec  3 20:34:12 2001
@@ -40,7 +40,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-//#define DEBUG
+#define DEBUG
 #include <linux/usb.h>
 
 #include "plusb.h"
@@ -457,7 +457,10 @@
 {
 	plusb_t *s = ptr;
 
+	printk ("plusb_net_disconnect: Starting\n");
+	
 	dbg("plusb_disconnect");
+	
 	s->connected = 0;
 	
 	plusb_free_all(s);
@@ -466,9 +469,16 @@
 		dbg("unregistering netdev: %s",s->net_dev.name);
 		unregister_netdev(&s->net_dev);
 		s->net_dev.name[0] = '\0';
+#if (LINUX_VERSION_CODE < 0x020300)		
+		kfree (s->net_dev.name);
+		s->net_dev.name = NULL;
+#endif
 	}
 	
 	dbg("plusb_disconnect: finished");
+	
+	printk ("plusb_net_disconnect: Finished\n");
+	
 	MOD_DEC_USE_COUNT;
 }
 
@@ -497,8 +507,14 @@
 {
 	plusb_t *s;
 
-	dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
-	  usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
+	printk ("plusb_probe: Starting\n");
+
+	if (usbdev) {
+		printk("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d\n",
+		    usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
+	} else {
+		printk ("plusb: usbdev is NULL!\n");
+	}
 
 	if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1)
 		return NULL;
@@ -507,22 +523,61 @@
 	if (usbdev->descriptor.bNumConfigurations != 1)
 		return NULL;
 
+	printk ("plusb_probe: Looking for Struct\n");
 	s = plusb_find_struct ();
 	if (!s)
 		return NULL;
 
 	s->usbdev = usbdev;
 
+	printk ("plusb_probe: Setting Configuration\n");
 	if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) {
 		err("set_configuration failed");
 		return NULL;
 	}
 
+	printk ("plusb_probe: Setting Interface\n");	
 	if (usb_set_interface (s->usbdev, 0, 0) < 0) {
 		err("set_interface failed");
 		return NULL;
 	}
 
+	printk ("plusb_probe: Checking device name\n");
+	
+#if (LINUX_VERSION_CODE < 0x020300)
+	{
+		int i;
+		
+		/* EZA: find the device number... we seem to have lost it...*/
+		for (i=0; i<NRPLUSB; i++) {
+			if (&plusb[i] == s)
+				break;
+		}
+	
+		/* EZA: for Kernel version 2.2, the driver is responsible for
+		   allocating this memory. For version 2.4, the rules
+		   have apparently changed, but there is a nifty function
+		   'init_netdev' that might make this easier...  It's in 
+		   ../net/net_init.c - but can we get there from here?  (no)
+		*/
+		if(!s->net_dev.name) {
+			s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL);
+			sprintf (s->net_dev.name, "plusb%d", i);
+			s->net_dev.init=plusb_net_init;
+			s->net_dev.priv=s;
+			
+			printk ("plusb_probe: Registering Device\n");	
+			if(!register_netdev(&s->net_dev))
+				info("registered: %s", s->net_dev.name);
+			else {
+				err("register_netdev failed");
+				s->net_dev.name[0] = '\0';
+			}
+			printk ("plusb_probe: Connected!\n");
+		}
+	}
+#else
+	/* Kernel version 2.3+ works a little bit differently  */
 	if(!s->net_dev.name[0]) {
 		strcpy(s->net_dev.name, "plusb%d");
 		s->net_dev.init=plusb_net_init;
@@ -534,8 +589,10 @@
 			s->net_dev.name[0] = '\0';
 		}
 	}
-		
+	
+#endif
 	s->connected = 1;
+	printk ("plusb_probe: Set Connected\n");		
 
 	if(s->opened) {
 		dbg("net device already allocated, restarting USB transfers");
@@ -560,19 +617,27 @@
 static int __init plusb_init (void)
 {
 	unsigned u;
+	
 	dbg("plusb_init");
 	
 	/* initialize struct */
 	for (u = 0; u < NRPLUSB; u++) {
-		plusb_t *s = &plusb[u];
+		plusb_t *s;
+		dbg("plusb_init: u=%ud about to assign s\n", u);
+		s= &plusb[u];
+		dbg("plusb_init: u=%ud about to memset\n", u);
 		memset (s, 0, sizeof (plusb_t));
 		s->bh.routine = (void (*)(void *))plusb_bh;
 		s->bh.data = s;
+		dbg("plusb_init: u=%ud about to init list head\n", u);
 		INIT_LIST_HEAD (&s->tx_skb_list);
 		INIT_LIST_HEAD (&s->free_skb_list);
+		dbg("plusb_init: u=%ud about to init spin lock\n", u);
 		spin_lock_init (&s->lock);
 	}
-
+	
+	dbg ("plusb_init: Initialized structure\n");
+	
 	/* register misc device */
 	usb_register (&plusb_driver);
 
diff -urN linux-2.2.18-pre15/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- linux-2.2.18-pre15/drivers/usb/usb-core.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/usb-core.c	Mon Dec  3 20:34:12 2001
@@ -19,27 +19,16 @@
 /*
  * USB core
  */
-
-int usb_hub_init(void);
-void usb_hub_cleanup(void);
-int usb_major_init(void);
-void usb_major_cleanup(void);
+extern int usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+extern int usb_major_init(void);
+extern void usb_major_cleanup(void);
 
 
 /*
- * HCI drivers
- */
-
-int uhci_init(void);
-int ohci_hcd_init(void);
-
-#ifdef MODULE
-
-/*
  * Cleanup
  */
-
-void cleanup_module(void)
+static void __exit usb_exit(void)
 {
 	usb_major_cleanup();
 	usbdevfs_cleanup();
@@ -49,20 +38,14 @@
 /*
  * Init
  */
-
-int init_module(void)
-#else
 int usb_init(void)
-#endif
 {
 	usb_major_init();
 	usbdevfs_init();
 	usb_hub_init();
 
-#ifndef CONFIG_USB_MODULE
-#ifdef CONFIG_USB_OHCI
-	ohci_hcd_init(); 
-#endif
-#endif
 	return 0;
 }
+
+module_init (usb_init);
+module_exit (usb_exit);
diff -urN linux-2.2.18-pre15/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- linux-2.2.18-pre15/drivers/usb/usb-ohci.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/usb-ohci.c	Mon Dec  3 20:34:12 2001
@@ -38,6 +38,7 @@
 #include <linux/malloc.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() */
@@ -2091,7 +2092,7 @@
 
 #define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
  
-int ohci_hcd_init (void) 
+static int __init ohci_hcd_init (void) 
 {
 	int ret = -ENODEV;
 	struct pci_dev * dev = NULL;
@@ -2108,15 +2109,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-#ifdef MODULE
-int init_module (void) 
-{
-	return ohci_hcd_init ();
-}
-
-/*-------------------------------------------------------------------------*/
-
-void cleanup_module (void) 
+static void __exit ohci_hcd_cleanup (void) 
 {	
 	ohci_t * ohci;
 	
@@ -2133,5 +2126,9 @@
 		hc_release_ohci (ohci);
 	}		
 }
-#endif //MODULE
 
+module_init (ohci_hcd_init);
+module_exit (ohci_hcd_cleanup);
+
+MODULE_AUTHOR ("Roman Weissgaerber <weissg@vienna.at>, David Brownell");
+MODULE_DESCRIPTION ("USB OHCI Host Controller Driver");
diff -urN linux-2.2.18-pre15/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.2.18-pre15/drivers/usb/usb.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/usb/usb.c	Mon Dec  3 20:34:12 2001
@@ -25,6 +25,21 @@
 #include <linux/bitops.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>  /* for in_interrupt() */
+
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* waitpid() call glue uses this */
+static int errno;
+#endif
+
+
 #ifdef CONFIG_USB_DEBUG
 	#define DEBUG
 #else
@@ -428,7 +443,7 @@
 void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
 {
 	/* this should never happen, don't release something that's not ours */
-	if (iface->driver != driver || !iface)
+	if (!iface || iface->driver != driver)
 		return;
 
 	iface->driver = NULL;
@@ -481,6 +496,222 @@
 	return -1;
 }
 
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ */
+
+static int exec_helper (void *arg)
+{
+	void **params = (void **) arg;
+	char *path = (char *) params [0];
+	char **argv = (char **) params [1];
+	char **envp = (char **) params [2];
+	return exec_usermodehelper (path, argv, envp);
+}
+
+int call_usermodehelper (char *path, char **argv, char **envp)
+{
+	void *params [3] = { path, argv, envp };
+	int pid, pid2, retval;
+	mm_segment_t fs;
+
+	if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) {
+		err ("failed fork of %s, errno = %d", argv [0], -pid);
+		return -1;
+	}
+
+	/* set signal mask? */
+	fs = get_fs ();
+	set_fs (KERNEL_DS);				/* retval is in kernel space. */
+	pid2 = waitpid (pid, &retval, __WCLONE);	/* "errno" gets assigned */
+	set_fs (fs);
+	/* restore signal mask? */
+
+	if (pid2 != pid) {
+		err ("waitpid(%d) failed, returned %d\n", pid, pid2);
+			return -1;
+	}
+	return retval;
+}
+
+static int to_bcd (char *buf, __u16 *bcdValue)
+{
+	int	retval = 0;
+	char	*value = (char *) bcdValue;
+	int	temp;
+
+	/* digits are 0-9 then ":;<=>?" for devices using
+	 * non-bcd (non-standard!) values here ... */
+
+	/* No leading (or later, trailing) zeroes since scripts do
+	 * literal matches, and that's how they're doing them. */
+	if ((temp = value [1] & 0xf0) != 0) {
+		temp >>= 4;
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+
+	temp = value [1] & 0x0f;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	*buf++ = '.';
+	retval++;
+
+	temp = value [0] & 0xf0;
+	temp >>= 4;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	if ((temp = value [0] & 0x0f) != 0) {
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+	*buf++ = 0;
+
+	return retval;
+}
+
+/*
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, or both.
+ *
+ * Some synchronization is important: removes can't start processing
+ * before the add-device processing completes, and vice versa.  That keeps
+ * a stack of USB-related identifiers stable while they're in use.  If we
+ * know that agents won't complete after they return (such as by forking
+ * a process that completes later), it's enough to just waitpid() for the
+ * agent -- as is currently done.
+ *
+ * The reason: we know we're called either from khubd (the typical case)
+ * or from root hub initialization (init, kapmd, modprobe, etc).  In both
+ * cases, we know no other thread can recycle our address, since we must
+ * already have been serialized enough to prevent that.
+ */
+static void call_policy (char *verb, struct usb_device *dev)
+{
+	char *argv [3], **envp, *buf, *scratch;
+	int i = 0, value;
+
+	if (!hotplug_path [0])
+		return;
+	if (in_interrupt ()) {
+		dbg ("In_interrupt");
+		return;
+	}
+	if (!current->fs->root) {
+		/* statically linked USB is initted rather early */
+		dbg ("call_policy %s, num %d -- no FS yet", verb, dev->devnum);
+		return;
+	}
+	if (dev->devnum < 0) {
+		dbg ("device already deleted ??");
+		return;
+	}
+	if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) {
+		dbg ("enomem");
+		return;
+	}
+	if (!(buf = kmalloc (256, GFP_KERNEL))) {
+		kfree (envp);
+		dbg ("enomem2");
+		return;
+	}
+
+	/* only one standardized param to hotplug command: type */
+	argv [0] = hotplug_path;
+	argv [1] = "usb";
+	argv [2] = 0;
+
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef	DEBUG
+	/* hint that policy agent should enter no-stdout debug mode */
+	envp [i++] = "DEBUG=kernel";
+#endif
+	/* extensible set of named bus-specific parameters,
+	 * supporting multiple driver selection algorithms.
+	 */
+	scratch = buf;
+
+	/* action:  add, remove */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "ACTION=%s", verb) + 1;
+
+#ifdef	CONFIG_USB_DEVICEFS
+	/* If this is available, userspace programs can directly read
+	 * all the device descriptors we don't tell them about.  Or
+	 * even act as usermode drivers.
+	 *
+	 * XXX how little intelligence can we hardwire?
+	 * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
+	 * (b) naming convention: bus1/device3, 001/003 etc.
+	 */
+	envp [i++] = "DEVFS=/proc/bus/usb";
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+		dev->bus->busnum, dev->devnum) + 1;
+#endif
+
+	/* per-device configuration hacks are often necessary */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+		dev->descriptor.idVendor,
+		dev->descriptor.idProduct);
+	scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+
+	/* otherwise, use a simple (so far) generic driver binding model */
+	envp [i++] = scratch;
+	if (dev->descriptor.bDeviceClass == 0) {
+		int alt = dev->actconfig->interface [0].act_altsetting;
+
+		/* simple/common case: one config, one interface, one driver
+		 * unsimple cases:  everything else
+		 */
+		scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+			+ 1;
+		/* INTERFACE-0, INTERFACE-1, ... ? */
+	} else {
+		/* simple/common case: generic device, handled generically */
+		scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+			dev->descriptor.bDeviceClass,
+			dev->descriptor.bDeviceSubClass,
+			dev->descriptor.bDeviceProtocol) + 1;
+	}
+	envp [i++] = 0;
+	/* assert: (scratch - buf) < sizeof buf */
+
+	/* NOTE: user mode daemons can call the agents too */
+
+	dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum);
+	value = call_usermodehelper (argv [0], argv, envp);
+	kfree (buf);
+	kfree (envp);
+	dbg ("kusbd policy returned 0x%x", value);
+}
+
+#else
+
+static inline void
+call_policy (char *verb, struct usb_device *dev)
+{ } 
+
+#endif	/* KMOD && HOTPLUG */
+
+
 /*
  * This entrypoint gets called for each new device.
  *
@@ -506,7 +737,10 @@
 		dbg("unhandled interfaces on device");
 
 	if (!claimed) {
-		warn("This device is not recognized by any installed USB driver.");
+		warn("USB device %d (prod/vend 0x%x/0x%x) is not claimed by any active driver.",
+			dev->devnum,
+			dev->descriptor.idVendor,
+			dev->descriptor.idProduct);
 #ifdef DEBUG
 		usb_show_device(dev);
 #endif
@@ -541,8 +775,8 @@
 void usb_free_dev(struct usb_device *dev)
 {
 	if (atomic_dec_and_test(&dev->refcnt)) {
-		usb_destroy_configuration(dev);
 		dev->bus->op->deallocate(dev);
+		usb_destroy_configuration(dev);
 		kfree(dev);
 	}
 }
@@ -585,7 +819,7 @@
 	if (urb && urb->dev)
 		return urb->dev->bus->op->submit_urb(urb);
 	else
-		return -1;
+		return -ENODEV;
 }
 
 /*-------------------------------------------------------------------*/
@@ -594,7 +828,7 @@
 	if (urb && urb->dev)
 		return urb->dev->bus->op->unlink_urb(urb);
 	else
-		return -1;
+		return -ENODEV;
 }
 /*-------------------------------------------------------------------*
  *                     COMPLETION HANDLERS                           *
@@ -1018,19 +1252,54 @@
 	size -= config->bLength;
 	
 	for (i = 0; i < config->bNumInterfaces; i++) {
-		header = (struct usb_descriptor_header *)buffer;
-		if ((header->bLength > size) || (header->bLength <= 2)) {
-			err("ran out of descriptors parsing");
-			return -1;
-		}
-		
-		if (header->bDescriptorType != USB_DT_INTERFACE) {
-			warn("unexpected descriptor 0x%X",
-				header->bDescriptorType);
+		int numskipped, len;
+		char *begin;
+
+		/* Skip over the rest of the Class Specific or Vendor */
+		/*  Specific descriptors */
+		begin = buffer;
+		numskipped = 0;
+		while (size >= sizeof(struct usb_descriptor_header)) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if ((header->bLength > size) || (header->bLength < 2)) {
+				err("invalid descriptor length of %d", header->bLength);
+				return -1;
+			}
+
+			/* If we find another descriptor which is at or below */
+			/*  us in the descriptor heirarchy then we're done  */
+			if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+			    (header->bDescriptorType == USB_DT_INTERFACE) ||
+			    (header->bDescriptorType == USB_DT_CONFIG) ||
+			    (header->bDescriptorType == USB_DT_DEVICE))
+				break;
+
+			dbg("skipping descriptor 0x%X", header->bDescriptorType);
+			numskipped++;
 
 			buffer += header->bLength;
 			size -= header->bLength;
-			continue;
+		}
+		if (numskipped)
+			dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+		/* Copy any unknown descriptors into a storage area for */
+		/*  drivers to later parse */
+		len = (int)(buffer - begin);
+		if (!len) {
+			config->extra = NULL;
+			config->extralen = 0;
+		} else {
+			config->extra = kmalloc(len, GFP_KERNEL);
+			if (!config->extra) {
+				err("couldn't allocate memory for config extra descriptors");
+				config->extralen = 0;
+				return -1;
+			}
+
+			memcpy(config->extra, begin, len);
+			config->extralen = len;
 		}
 
 		retval = usb_parse_interface(dev, config->interface + i, buffer, size);
@@ -1096,13 +1365,6 @@
 	}
 	kfree(dev->config);
 }
-			
-void usb_init_root_hub(struct usb_device *dev)
-{
-	dev->devnum = -1;
-	dev->slow = 0;
-	dev->actconfig = NULL;
-}
 
 /* for returning string descriptors in UTF-16LE */
 static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
@@ -1195,6 +1457,8 @@
 
 	info("USB disconnect on device %d", dev->devnum);
 
+	call_policy ("remove", dev);
+
 	if (dev->actconfig) {
 		for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
 			struct usb_interface *interface = &dev->actconfig->interface[i];
@@ -1215,13 +1479,13 @@
 			usb_disconnect(child);
 	}
 
-	/* remove /proc/bus/usb entry */
-	usbdevfs_remove_device(dev);
-
-	/* Free up the device itself, including its device number */
-	if (dev->devnum > 0)
+	/* Free the device number and remove the /proc/bus/usb entry */
+	if (dev->devnum > 0) {
 		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		usbdevfs_remove_device(dev);
+	}
 
+	/* Free up the device itself */
 	usb_free_dev(dev);
 }
 
@@ -1395,7 +1659,7 @@
 	if (result < 0)
 		return result;
 
-	if (status & 1)
+	if (le16_to_cpu(status) & 1)
 		return -EPIPE;		/* still halted */
 
 	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
@@ -1443,7 +1707,7 @@
 	}
 	if (!cp) {
 		warn("selecting invalid configuration %d", configuration);
-		return -1;
+		return -EINVAL;
 	}
 
 	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
@@ -1483,12 +1747,12 @@
 
 	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
 		warn("too many configurations");
-		return -1;
+		return -EINVAL;
 	}
 
 	if (dev->descriptor.bNumConfigurations < 1) {
 		warn("not enough configurations");
-		return -1;
+		return -EINVAL;
 	}
 
 	dev->config = (struct usb_config_descriptor *)
@@ -1496,7 +1760,7 @@
 		sizeof(struct usb_config_descriptor), GFP_KERNEL);
 	if (!dev->config) {
 		err("out of memory");
-		return -1;	
+		return -ENOMEM;	
 	}
 	memset(dev->config, 0, dev->descriptor.bNumConfigurations *
 		sizeof(struct usb_config_descriptor));
@@ -1505,7 +1769,7 @@
 		dev->descriptor.bNumConfigurations, GFP_KERNEL);
 	if (!dev->rawdescriptors) {
 		err("out of memory");
-		return -1;
+		return -ENOMEM;
 	}
 
 	for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
@@ -1515,8 +1779,10 @@
 		if (result < 8) {
 			if (result < 0)
 				err("unable to get descriptor");
-			else
+			else {
 				err("config descriptor too short (expected %i, got %i)", 8, result);
+				result = -EINVAL;
+			}
 			goto err;
 		}
 
@@ -1540,6 +1806,7 @@
 	
 		if (result < length) {
 			err("config descriptor too short (expected %i, got %i)", length, result);
+			result = -EINVAL;
 			kfree(bigbuffer);
 			goto err;
 		}
@@ -1550,7 +1817,7 @@
 		if (result > 0)
 			dbg("descriptor data left");
 		else if (result < 0) {
-			result = -1;
+			result = -EINVAL;
 			goto err;
 		}
 	}
@@ -1680,18 +1947,19 @@
 	err = usb_get_configuration(dev);
 	if (err < 0) {
 		err("unable to get configuration (error=%d)", err);
+		usb_destroy_configuration(dev);
 		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
 
-	dev->actconfig = dev->config;
-	usb_set_maxpacket(dev);
-
 	/* we set the default configuration here */
-	if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
-		err("failed to set default configuration");
-		return -1;
+	err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+	if (err) {
+		err("failed to set default configuration (error=%d)", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
 	}
 
 	dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
@@ -1711,6 +1979,9 @@
 	/* find drivers willing to handle this device */
 	usb_find_drivers(dev);
 
+	/* userspace may load modules and/or configure further */
+	call_policy ("add", dev);
+
 	return 0;
 }
 
@@ -1782,7 +2053,6 @@
 EXPORT_SYMBOL(usb_interface_claimed);
 EXPORT_SYMBOL(usb_driver_release_interface);
 
-EXPORT_SYMBOL(usb_init_root_hub);
 EXPORT_SYMBOL(usb_root_hub_string);
 EXPORT_SYMBOL(usb_new_device);
 EXPORT_SYMBOL(usb_reset_device);
diff -urN linux-2.2.18-pre15/drivers/video/Config.in linux/drivers/video/Config.in
--- linux-2.2.18-pre15/drivers/video/Config.in	Mon Dec  3 20:33:36 2001
+++ linux/drivers/video/Config.in	Mon Dec  3 20:34:12 2001
@@ -66,6 +66,8 @@
     fi
   fi
   if [ "$CONFIG_MAC" = "y" ]; then
+    bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE
+#    bool 'Apple DAFB display support' CONFIG_FB_DAFB
     define_bool CONFIG_FB_MAC y
   fi
   if [ "$CONFIG_HP300" = "y" ]; then
diff -urN linux-2.2.18-pre15/drivers/video/Makefile linux/drivers/video/Makefile
--- linux-2.2.18-pre15/drivers/video/Makefile	Mon Dec  3 20:33:36 2001
+++ linux/drivers/video/Makefile	Mon Dec  3 20:34:12 2001
@@ -153,7 +153,7 @@
 endif
 
 ifeq ($(CONFIG_FB_MAC),y)
-L_OBJS += macfb.o
+L_OBJS += macfb.o macmodes.o
 endif
 
 ifeq ($(CONFIG_FB_HP300),y)
diff -urN linux-2.2.18-pre15/drivers/video/cyberfb.c linux/drivers/video/cyberfb.c
--- linux-2.2.18-pre15/drivers/video/cyberfb.c	Tue Jan 19 10:47:48 1999
+++ linux/drivers/video/cyberfb.c	Mon Dec  3 20:34:12 2001
@@ -109,7 +109,10 @@
 
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
-#define wb_64(reg,dat) (*((unsigned char volatile *)CyberRegs + reg) = dat)
+#define wb_64(regs,reg,dat) (*(((volatile unsigned char *)regs) + reg) = dat)
+#define rb_64(regs, reg) (*(((volatile unsigned char *)regs) + reg))
+
+#define ww_64(regs,reg,dat) (*((volatile unsigned short *)(regs + reg) = dat)
 
 struct cyberfb_par {
 	struct fb_var_screeninfo var;
@@ -129,58 +132,27 @@
 
 
 /*
-*    Switch for Chipset Independency
-*/
-
-static struct fb_hwswitch {
-
-/* Initialisation */
-
-int (*init)(void);
-
-/* Display Control */
-
-int (*encode_fix)(struct fb_fix_screeninfo *fix, struct cyberfb_par *par);
-int (*decode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par);
-int (*encode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par);
-int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
-		 u_int *transp, struct fb_info *info);
-int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
-		 u_int transp, struct fb_info *info);
-void (*blank)(int blank);
-} *fbhw;
-
-
-/*
-*    Frame Buffer Name
-*/
+ *    Frame Buffer Name
+ */
 
 static char cyberfb_name[16] = "Cybervision";
 
 
 /*
-*    Cybervision Graphics Board
-*/
+ *    CyberVision Graphics Board
+ */
 
 static unsigned char Cyber_colour_table [256][3];
-static unsigned long CyberMem;
 static unsigned long CyberSize;
-static volatile char *CyberRegs;
+static volatile unsigned char *CyberBase;
+static volatile unsigned char *CyberMem;
+static volatile unsigned char *CyberRegs;
 static unsigned long CyberMem_phys;
 static unsigned long CyberRegs_phys;
-/* From cvision.c  for cvision_core.c */
-static unsigned long cv64_mem;
-static unsigned long cv64_fbmem;
-static volatile char *cv64_regs;
-static unsigned long cv64_size;
-#if 0
-static int cvision_custom_mode = 0;
-static int hbs, hbe, hss, hse, ht, vbs, vbe, vss, vse, vt;
-#endif
 
 /*
-*    Predefined Video Modes
-*/
+ *    Predefined Video Modes
+ */
 
 static struct fb_videomode cyberfb_predefined[] __initdata = {
 	{ "640x480-8", {		/* Default 8 BPP mode (cyber8) */
@@ -251,17 +223,18 @@
 static int Cyberfb_inverse = 0;
 
 /*
-*    Some default modes
-*/
+ *    Some default modes
+ */
 
 #define CYBER8_DEFMODE     (0)
 #define CYBER16_DEFMODE    (1)
 
 static struct fb_var_screeninfo cyberfb_default;
+static int cyberfb_usermode __initdata = 0;
 
 /*
-*    Interface used by the world
-*/
+ *    Interface used by the world
+ */
 
 void cyberfb_setup(char *options, int *ints);
 
@@ -283,8 +256,8 @@
 			 u_long arg, int con, struct fb_info *info);
 
 /*
-*    Interface to the low level console driver
-*/
+ *    Interface to the low level console driver
+ */
 
 void cyberfb_init(void);
 static int Cyberfb_switch(int con, struct fb_info *info);
@@ -292,16 +265,16 @@
 static void Cyberfb_blank(int blank, struct fb_info *info);
 
 /*
-*    Text console acceleration
-*/
+ *    Text console acceleration
+ */
 
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_cyber8;
 #endif
 
 /*
-*    Accelerated Functions used by the low level console driver
-*/
+ *    Accelerated Functions used by the low level console driver
+ */
 
 static void Cyber_WaitQueue(u_short fifo);
 static void Cyber_WaitBlit(void);
@@ -310,11 +283,13 @@
 			 u_short mode);
 static void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height,
 			   u_short mode, u_short color);
+#if 0
 static void Cyber_MoveCursor(u_short x, u_short y);
+#endif
 
 /*
-*   Hardware Specific Routines
-*/
+ *   Hardware Specific Routines
+ */
 
 static int Cyber_init(void);
 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
@@ -327,11 +302,10 @@
 			   u_int *transp, struct fb_info *info);
 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 			   u_int transp, struct fb_info *info);
-static void Cyber_blank(int blank);
 
 /*
-*    Internal routines
-*/
+ *    Internal routines
+ */
 
 static void cyberfb_get_par(struct cyberfb_par *par);
 static void cyberfb_set_par(struct cyberfb_par *par);
@@ -342,7 +316,7 @@
 
 /* For cvision_core.c */
 static unsigned short cv64_compute_clock(unsigned long);
-static int cv_has_4mb (volatile caddr_t);
+static int cv_has_4mb (volatile unsigned char *);
 static void cv64_board_init (void);
 static void cv64_load_video_mode (struct fb_var_screeninfo *);
 
@@ -351,16 +325,17 @@
 
 
 /*
-*    Initialization
-*
-*    Set the default video mode for this chipset. If a video mode was
-*    specified on the command line, it will override the default mode.
-*/
+ *    Initialization
+ *
+ *    Set the default video mode for this chipset. If a video mode was
+ *    specified on the command line, it will override the default mode.
+ */
 
 static int Cyber_init(void)
 {
+	volatile unsigned char *regs = CyberRegs;
+	volatile unsigned long *CursorBase;
 	int i;
-	volatile u_long *CursorBase;
 	DPRINTK("ENTER\n");
 
 /* Init local cmap as greyscale levels */
@@ -371,27 +346,25 @@
 	}
 
 /* Initialize the board and determine fbmem size */
-	cv64_board_init (); 
+	cv64_board_init(); 
 #ifdef CYBERFBDEBUG
 	DPRINTK("Register state after initing board\n");
 	cv64_dump();
 #endif
 /* Clear framebuffer memory */
 	DPRINTK("Clear framebuffer memory\n");
-	memset ((char *) cv64_fbmem, 0, cv64_size);
+	memset ((char *)CyberMem, 0, CyberSize);
 
 /* Disable hardware cursor */
 	DPRINTK("Disable HW cursor\n");
-	wb_64(S3_CRTC_ADR, S3_REG_LOCK2);
-	wb_64(S3_CRTC_DATA, 0xa0);
-	wb_64(S3_CRTC_ADR, S3_HGC_MODE);
-	wb_64(S3_CRTC_DATA, 0x00);
-	wb_64(S3_CRTC_ADR, S3_HWGC_DX);
-	wb_64(S3_CRTC_DATA, 0x00);
-	wb_64(S3_CRTC_ADR, S3_HWGC_DY);
-	wb_64(S3_CRTC_DATA, 0x00);
-
-	CyberSize = cv64_size;
+	wb_64(regs, S3_CRTC_ADR, S3_REG_LOCK2);
+	wb_64(regs, S3_CRTC_DATA, 0xa0);
+	wb_64(regs, S3_CRTC_ADR, S3_HGC_MODE);
+	wb_64(regs, S3_CRTC_DATA, 0x00);
+	wb_64(regs, S3_CRTC_ADR, S3_HWGC_DX);
+	wb_64(regs, S3_CRTC_DATA, 0x00);
+	wb_64(regs, S3_CRTC_ADR, S3_HWGC_DY);
+	wb_64(regs, S3_CRTC_DATA, 0x00);
 
 /* Initialize hardware cursor */
 	DPRINTK("Init HW cursor\n");
@@ -420,9 +393,9 @@
 
 
 /*
-*    This function should fill in the `fix' structure based on the
-*    values in the `par' structure.
-*/
+ *    This function should fill in the `fix' structure based on the
+ *    values in the `par' structure.
+ */
 
 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
 			    struct cyberfb_par *par)
@@ -546,19 +519,21 @@
 
 
 /*
-*    Set a single color register. Return != 0 for invalid regno.
-*/
+ *    Set a single color register. Return != 0 for invalid regno.
+ */
 
 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 			   u_int transp, struct fb_info *info)
 {
+	volatile unsigned char *regs = CyberRegs;
+
 	/*DPRINTK("ENTER\n");*/
 	if (regno > 255) {
 		DPRINTK("EXIT - Register # > 255\n");
 		return (1);
 	}
 
-	wb_64(0x3c8, (unsigned char) regno);
+	wb_64(regs, 0x3c8, (unsigned char) regno);
 
  	red >>= 10;
  	green >>= 10;
@@ -568,9 +543,9 @@
 	Cyber_colour_table [regno][1] = green;
 	Cyber_colour_table [regno][2] = blue;
 
-	wb_64(0x3c9, red);
-	wb_64(0x3c9, green);
-	wb_64(0x3c9, blue);
+	wb_64(regs, 0x3c9, red);
+	wb_64(regs, 0x3c9, green);
+	wb_64(regs, 0x3c9, blue);
 
 	/*DPRINTK("EXIT\n");*/
 	return (0);
@@ -611,29 +586,30 @@
 *           0 = restore fb cmap from local cmap
 */
 
-void Cyber_blank(int blank)
+void Cyberfb_blank(int blank, struct fb_info *info)
 {
+	volatile unsigned char *regs = CyberRegs;
 	int i;
 
 	DPRINTK("ENTER\n");
 #if 0
 /* Blank by turning gfx off */
-	gfx_on_off (1, cv64_regs);
+	gfx_on_off (1, regs);
 #else
 	if (blank) {
 		for (i = 0; i < 256; i++) {
-			wb_64(0x3c8, (unsigned char) i);
+			wb_64(regs, 0x3c8, (unsigned char) i);
 			/* ARB Pale red to detect this blanking method */
-			wb_64(0x3c9, 48); 
-			wb_64(0x3c9, 0);
-			wb_64(0x3c9, 0);
+			wb_64(regs, 0x3c9, 48); 
+			wb_64(regs, 0x3c9, 0);
+			wb_64(regs, 0x3c9, 0);
 		}
 	} else {
 		for (i = 0; i < 256; i++) {
-			wb_64(0x3c8, (unsigned char) i);
-			wb_64(0x3c9, Cyber_colour_table[i][0]);
-			wb_64(0x3c9, Cyber_colour_table[i][1]);
-			wb_64(0x3c9, Cyber_colour_table[i][2]);
+			wb_64(regs, 0x3c8, (unsigned char) i);
+			wb_64(regs, 0x3c9, Cyber_colour_table[i][0]);
+			wb_64(regs, 0x3c9, Cyber_colour_table[i][1]);
+			wb_64(regs, 0x3c9, Cyber_colour_table[i][2]);
 		}
 	}
 #endif
@@ -646,13 +622,12 @@
  */
 static void Cyber_WaitQueue (u_short fifo)
 {
-	u_short status;
+	unsigned short status;
 
 	DPRINTK("ENTER\n");
 	do {
 		status = *((u_short volatile *)(CyberRegs + S3_GP_STAT));
-	}
-	while (status & fifo);
+	} while (status & fifo);
 	DPRINTK("EXIT\n");
 }
 
@@ -661,13 +636,12 @@
  */
 static void Cyber_WaitBlit (void)
 {
-	u_short status;
+	unsigned short status;
 
 	DPRINTK("ENTER\n");
 	do {
 		status = *((u_short volatile *)(CyberRegs + S3_GP_STAT));
-	}
-	while (status & S3_HDW_BUSY);
+	} while (status & S3_HDW_BUSY);
 	DPRINTK("EXIT\n");
 }
 
@@ -678,6 +652,7 @@
 			  u_short desty, u_short width, u_short height,
 			  u_short mode)
 {
+	volatile unsigned char *regs = CyberRegs;
 	u_short blitcmd = S3_BITBLT;
 
 	DPRINTK("ENTER\n");
@@ -699,19 +674,19 @@
 
 	Cyber_WaitQueue (0x8000);
 
-	*((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000;
-	*((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0060 | mode);
+	*((u_short volatile *)(regs + S3_PIXEL_CNTL)) = 0xa000;
+	*((u_short volatile *)(regs + S3_FRGD_MIX)) = (0x0060 | mode);
 
-	*((u_short volatile *)(CyberRegs + S3_CUR_X)) = curx;
-	*((u_short volatile *)(CyberRegs + S3_CUR_Y)) = cury;
+	*((u_short volatile *)(regs + S3_CUR_X)) = curx;
+	*((u_short volatile *)(regs + S3_CUR_Y)) = cury;
 
-	*((u_short volatile *)(CyberRegs + S3_DESTX_DIASTP)) = destx;
-	*((u_short volatile *)(CyberRegs + S3_DESTY_AXSTP)) = desty;
+	*((u_short volatile *)(regs + S3_DESTX_DIASTP)) = destx;
+	*((u_short volatile *)(regs + S3_DESTY_AXSTP)) = desty;
 
-	*((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1;
-	*((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width  - 1;
+	*((u_short volatile *)(regs + S3_MIN_AXIS_PCNT)) = height - 1;
+	*((u_short volatile *)(regs + S3_MAJ_AXIS_PCNT)) = width  - 1;
 
-	*((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd;
+	*((u_short volatile *)(regs + S3_CMD)) = blitcmd;
 	DPRINTK("EXIT\n");
 }
 
@@ -721,56 +696,52 @@
 static void Cyber_RectFill (u_short x, u_short y, u_short width,
 			    u_short height, u_short mode, u_short color)
 {
+	volatile unsigned char *regs = CyberRegs;
 	u_short blitcmd = S3_FILLEDRECT;
 
 	DPRINTK("ENTER\n");
 	Cyber_WaitQueue (0x8000);
 
-	*((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000;
-	*((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0020 | mode);
+	*((u_short volatile *)(regs + S3_PIXEL_CNTL)) = 0xa000;
+	*((u_short volatile *)(regs + S3_FRGD_MIX)) = (0x0020 | mode);
 
-	*((u_short volatile *)(CyberRegs + S3_MULT_MISC)) = 0xe000;
-	*((u_short volatile *)(CyberRegs + S3_FRGD_COLOR)) = color;
+	*((u_short volatile *)(regs + S3_MULT_MISC)) = 0xe000;
+	*((u_short volatile *)(regs + S3_FRGD_COLOR)) = color;
 
-	*((u_short volatile *)(CyberRegs + S3_CUR_X)) = x;
-	*((u_short volatile *)(CyberRegs + S3_CUR_Y)) = y;
+	*((u_short volatile *)(regs + S3_CUR_X)) = x;
+	*((u_short volatile *)(regs + S3_CUR_Y)) = y;
 
-	*((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1;
-	*((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width  - 1;
+	*((u_short volatile *)(regs + S3_MIN_AXIS_PCNT)) = height - 1;
+	*((u_short volatile *)(regs + S3_MAJ_AXIS_PCNT)) = width  - 1;
 
-	*((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd;
+	*((u_short volatile *)(regs + S3_CMD)) = blitcmd;
 	DPRINTK("EXIT\n");
 }
 
+
+#if 0
 /**************************************************************
  * Move cursor to x, y
  */
 static void Cyber_MoveCursor (u_short x, u_short y)
 {
+	volatile unsigned char *regs = CyberRegs;
 	DPRINTK("ENTER\n");
-	*(CyberRegs + S3_CRTC_ADR)  = 0x39;
-	*(CyberRegs + S3_CRTC_DATA) = 0xa0;
+	*(regs + S3_CRTC_ADR)  = 0x39;
+	*(regs + S3_CRTC_DATA) = 0xa0;
 
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_ORGX_H;
-	*(CyberRegs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8);
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_ORGX_L;
-	*(CyberRegs + S3_CRTC_DATA) = (char)(x & 0x00ff);
-
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_ORGY_H;
-	*(CyberRegs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8);
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_ORGY_L;
-	*(CyberRegs + S3_CRTC_DATA) = (char)(y & 0x00ff);
+	*(regs + S3_CRTC_ADR)  = S3_HWGC_ORGX_H;
+	*(regs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8);
+	*(regs + S3_CRTC_ADR)  = S3_HWGC_ORGX_L;
+	*(regs + S3_CRTC_DATA) = (char)(x & 0x00ff);
+
+	*(regs + S3_CRTC_ADR)  = S3_HWGC_ORGY_H;
+	*(regs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8);
+	*(regs + S3_CRTC_ADR)  = S3_HWGC_ORGY_L;
+	*(regs + S3_CRTC_DATA) = (char)(y & 0x00ff);
 	DPRINTK("EXIT\n");
 }
-
-
-/* -------------------- Interfaces to hardware functions -------------------- */
-
-
-static struct fb_hwswitch Cyber_switch = {
-	Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
-	Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
-};
+#endif
 
 
 /* -------------------- Generic routines ---------------------------------- */
@@ -786,7 +757,7 @@
 	if (current_par_valid) {
 		*par = current_par;
 	} else {
-		fbhw->decode_var(&cyberfb_default, par);
+		Cyber_decode_var(&cyberfb_default, par);
 	}
 	DPRINTK("EXIT\n");
 }
@@ -819,14 +790,14 @@
 	struct cyberfb_par par;
 
 	DPRINTK("ENTER\n");
-	if ((err = fbhw->decode_var(var, &par))) {
+	if ((err = Cyber_decode_var(var, &par))) {
 		DPRINTK("EXIT - decode_var failed\n");
 		return(err);
 	}
 	activate = var->activate;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
 		cyberfb_set_par(&par);
-	fbhw->encode_var(var, &par);
+	Cyber_encode_var(var, &par);
 	var->activate = activate;
 
 	cyber_set_video(var);
@@ -844,11 +815,11 @@
 	}
 	if (fb_display[con].cmap.len) {
 		DPRINTK("Use console cmap\n");
-		fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info);
+		fb_set_cmap(&fb_display[con].cmap, 1, Cyber_setcolreg, info);
 	} else {
 		DPRINTK("Use default cmap\n");
 		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			    1, fbhw->setcolreg, info);
+			    1, Cyber_setcolreg, info);
 	}
 	DPRINTK("EXIT\n");
 }
@@ -889,10 +860,10 @@
 	if (con == -1) {
 		cyberfb_get_par(&par);
 	} else {
-		error = fbhw->decode_var(&fb_display[con].var, &par);
+		error = Cyber_decode_var(&fb_display[con].var, &par);
 	}
 	DPRINTK("EXIT\n");
-	return(error ? error : fbhw->encode_fix(fix, &par));
+	return(error ? error : Cyber_encode_fix(fix, &par));
 }
 
 
@@ -909,7 +880,7 @@
 	DPRINTK("ENTER\n");
 	if (con == -1) {
 		cyberfb_get_par(&par);
-		error = fbhw->encode_var(var, &par);
+		error = Cyber_encode_var(var, &par);
 		disp.var = *var;   /* ++Andre: don't know if this is the right place */
 	} else {
 		*var = fb_display[con].var;
@@ -934,7 +905,7 @@
 	cyberfb_get_fix(&fix, con, info);
 	if (con == -1)
 		con = 0;
-	display->screen_base = phys_to_virt ((unsigned long) fix.smem_start);
+	display->screen_base = (unsigned char *)CyberMem;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -1014,7 +985,7 @@
 	DPRINTK("ENTER\n");
 	if (con == currcon) { /* current console? */
 		DPRINTK("EXIT - console is current console\n");
-		return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info));
+		return(fb_get_cmap(cmap, kspc, Cyber_getcolreg, info));
 	} else if (fb_display[con].cmap.len) { /* non default colormap? */
 		DPRINTK("Use console cmap\n");
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1048,7 +1019,7 @@
 	}
 	if (con == currcon) {		 /* current console? */
 		DPRINTK("EXIT - Current console\n");
-		return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info));
+		return(fb_set_cmap(cmap, kspc, Cyber_setcolreg, info));
 	} else {
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	}
@@ -1066,7 +1037,7 @@
 static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con,
 			       struct fb_info *info)
 {
-	return(-EINVAL);
+	return -EINVAL;
 }
 
 
@@ -1077,7 +1048,7 @@
 static int cyberfb_ioctl(struct inode *inode, struct file *file,
 			 u_int cmd, u_long arg, int con, struct fb_info *info)
 {
-	return(-EINVAL);
+	return -EINVAL;
 }
 
 
@@ -1109,8 +1080,10 @@
 			strcpy(fb_info.fontname, this_opt+5);
 		} else if (!strcmp (this_opt, "cyber8")) {
 			cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var;
+			cyberfb_usermode = 1;
 		} else if (!strcmp (this_opt, "cyber16")) {
 			cyberfb_default = cyberfb_predefined[CYBER16_DEFMODE].var;
+			cyberfb_usermode = 1;
 		} else get_video_mode(this_opt);
 	}
 
@@ -1146,14 +1119,12 @@
 	DPRINTK("board_addr=%08lx\n", board_addr);
 	DPRINTK("board_size=%08lx\n", board_size);
 
-	cv64_mem = ioremap(board_addr, board_size);
-	cv64_regs = (volatile char *)(cv64_mem + 0x02000000);
-	cv64_fbmem = cv64_mem + 0x01400000;
-	DPRINTK("cv64_mem=%08lx cv64_regs=%08lx cv64_fbmem=%08lx\n",
-		cv64_mem, (long unsigned int)cv64_regs, cv64_fbmem);
+	CyberBase = ioremap(board_addr, board_size);
+	CyberRegs = CyberBase + 0x02000000;
+	CyberMem = CyberBase + 0x01400000;
+	DPRINTK("CyberBase=%08lx CyberRegs=%08lx CyberMem=%08lx\n",
+		CyberBase, (long unsigned int)CyberRegs, CyberMem);
 
-	CyberMem = cv64_fbmem;
-	CyberRegs = cv64_regs;
 	CyberMem_phys = board_addr + 0x01400000;
 	CyberRegs_phys = CyberMem_phys + 0x00c00000;
 	DPRINTK("CyberMem=%08lx CyberRegs=%08lx\n", CyberMem,
@@ -1164,8 +1135,6 @@
 	cv64_dump();
 #endif
 
-	fbhw = &Cyber_switch;
-
 	strcpy(fb_info.modename, cyberfb_name);
 	fb_info.changevar = NULL;
 	fb_info.node = -1;
@@ -1175,9 +1144,14 @@
 	fb_info.updatevar = &Cyberfb_updatevar;
 	fb_info.blank = &Cyberfb_blank;
 
-	fbhw->init();
-	fbhw->decode_var(&cyberfb_default, &par);
-	fbhw->encode_var(&cyberfb_default, &par);
+	Cyber_init();
+	/* ++Andre: set cyberfb default mode */
+	if (!cyberfb_usermode) {
+		cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var;
+		DPRINTK("Use default cyber8 mode\n");
+	}
+	Cyber_decode_var(&cyberfb_default, &par);
+	Cyber_encode_var(&cyberfb_default, &par);
 
 	do_fb_set_var(&cyberfb_default, 1);
 	cyberfb_get_var(&fb_display[0].var, -1, &fb_info);
@@ -1203,7 +1177,7 @@
         DPRINTK("ENTER\n");
 	/* Do we have to save the colormap? */
 	if (fb_display[currcon].cmap.len) {
-		fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
+		fb_get_cmap(&fb_display[currcon].cmap, 1, Cyber_getcolreg,
 			    info);
 	}
 
@@ -1231,18 +1205,6 @@
 
 
 /*
- *    Blank the display.
- */
-
-static void Cyberfb_blank(int blank, struct fb_info *info)
-{
-	DPRINTK("Enter\n");
-	fbhw->blank(blank);
-	DPRINTK("Exit\n");
-}
-
-
-/*
  *    Get a Video Mode
  */
 
@@ -1254,13 +1216,11 @@
 	for (i = 0; i < NUM_TOTAL_MODES; i++) {
 		if (!strcmp(name, cyberfb_predefined[i].name)) {
 			cyberfb_default = cyberfb_predefined[i].var;
+			cyberfb_usermode = 1;
 			DPRINTK("EXIT - Matched predefined mode\n");
 			return(i);
 		}
 	}
-	/* ++Andre: set cyberfb default mode */
-	cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var;
-	DPRINTK("EXIT - Use default cyber8 mode\n");
 	return(0);
 }
 
@@ -1454,71 +1414,38 @@
 };
 
 /* -------------------- Hardware specific routines ------------------------- */
-#if 0
-/* ARB Generates 100 usec delay */
-inline void __cv_delay (unsigned long usecs)
-{
-	int k;
-	
-	for (k = 0; k < 1000; k++) {
-		asm volatile ("nop");
-	}
-}
-#endif
-
-/* Wait while Graphics Engine is busy */
-inline void GfxBusyWait (volatile caddr_t board)
-{
-	int test;
-	DPRINTK("ENTER\n");
-	
-	do {
-		test = vgar16 (board, ECR_GP_STAT);
-		asm volatile ("nop");
-	} while (test & (1 << 9));
-	DPRINTK("EXIT\n");
-}
-
-/* Wait for any of the 8 Trio32 FIFOs to be free */ 
-inline void GfxFifoWait (volatile caddr_t board)
-{
-	int test;
-	DPRINTK("ENTER\n");
-	
-	do {
-		test = vgar16 (board, ECR_GP_STAT);
-	} while (test & 0x0f);
-	DPRINTK("EXIT\n");
-}
 
 /* Read Attribute Controller Register=idx */
-inline unsigned char RAttr (volatile caddr_t board, short idx)
+inline unsigned char RAttr (volatile unsigned char *regs, short idx)
 {
-	vgaw (board, ACT_ADDRESS_W, idx);
+	wb_64 (regs, ACT_ADDRESS_W, idx);
+	mb();
 	udelay(100);
-	/* __cv_delay (0); */
-	return (vgar (board, ACT_ADDRESS_R));
+	return (rb_64(regs, ACT_ADDRESS_R));
 }
 
 /* Read Sequencer Register=idx */
-inline unsigned char RSeq (volatile caddr_t board, short idx)
+inline unsigned char RSeq (volatile unsigned char *regs, short idx)
 {
-	vgaw (board, SEQ_ADDRESS, idx);
-	return (vgar (board, SEQ_ADDRESS_R));
+	wb_64 (regs, SEQ_ADDRESS, idx);
+	mb();
+	return (rb_64(regs, SEQ_ADDRESS_R));
 }
 
 /* Read CRT Controller Register=idx */
-inline unsigned char RCrt (volatile caddr_t board, short idx)
+inline unsigned char RCrt (volatile unsigned char *regs, short idx)
 {
-	vgaw (board, CRT_ADDRESS, idx);
-	return (vgar (board, CRT_ADDRESS_R));
+	wb_64 (regs, CRT_ADDRESS, idx);
+	mb();
+	return (rb_64(regs, CRT_ADDRESS_R));
 }
 
 /* Read Graphics Controller Register=idx */
-inline unsigned char RGfx (volatile caddr_t board, short idx)
+inline unsigned char RGfx (volatile unsigned char *regs, short idx)
 {
-	vgaw (board, GCT_ADDRESS, idx);
-	return (vgar (board, GCT_ADDRESS_R));
+	wb_64 (regs, GCT_ADDRESS, idx);
+	mb();
+	return (rb_64(regs, GCT_ADDRESS_R));
 }
 
 /*
@@ -1526,13 +1453,13 @@
  */
 
 inline void cv64_write_port (unsigned short bits,
-			     volatile unsigned char *board)
+			     volatile unsigned char *base)
 {
 	volatile unsigned char *addr;
 	static unsigned char cvportbits = 0; /* Mirror port bits here */
 	DPRINTK("ENTER\n");
 
-	addr = board + 0x40001;
+	addr = base + 0x40001;
 	if (bits & 0x8000) {
 		cvportbits |= bits & 0xff; /* Set bits */
 		DPRINTK("Set bits: %04x\n", bits);
@@ -1542,7 +1469,7 @@
 		cvportbits &= bits; /* Clear bits */
 		DPRINTK("Clear bits: %04x\n", bits);
 	}
-	
+
 	*addr = cvportbits;
 	DPRINTK("EXIT\n");
 }
@@ -1572,7 +1499,7 @@
 /* Control screen display */
 /* toggle: 0 = on, 1 = off */
 /* board = registerbase */
-inline void gfx_on_off (int toggle, volatile unsigned char *board)
+inline void gfx_on_off(int toggle, volatile unsigned char *regs)
 {
 	int r;
 	DPRINTK("ENTER\n");
@@ -1581,10 +1508,10 @@
 	toggle = toggle << 5;
 	DPRINTK("Turn display %s\n", (toggle ? "off" : "on"));
 	
-	r = (int) RSeq ((volatile caddr_t) board, SEQ_ID_CLOCKING_MODE);
+	r = (int) RSeq(regs, SEQ_ID_CLOCKING_MODE);
 	r &= 0xdf;	/* Set bit 5 to 0 */
 	
-	WSeq (board, SEQ_ID_CLOCKING_MODE, r | toggle);
+	WSeq (regs, SEQ_ID_CLOCKING_MODE, r | toggle);
 	DPRINTK("EXIT\n");
 }
 
@@ -1640,11 +1567,11 @@
 	return (erg);
 }
 
-static int cv_has_4mb (volatile caddr_t fb)
+static int cv_has_4mb (volatile unsigned char *fb)
 {
 	volatile unsigned long *tr, *tw;
 	DPRINTK("ENTER\n");
-	
+
 	/* write patterns in memory and test if they can be read */
 	tw = (volatile unsigned long *) fb;
 	tr = (volatile unsigned long *) (fb + 0x02000000);
@@ -1655,41 +1582,42 @@
 		DPRINTK("EXIT - <4MB\n");
 		return (0);
 	}
-	
+
 	/* upper memory region */
 	tw = (volatile unsigned long *) (fb + 0x00200000);
 	tr = (volatile unsigned long *) (fb + 0x02200000);
-	
+
 	*tw = 0x87654321;
-	
+
 	if (*tr != 0x87654321) {
 		DPRINTK("EXIT - <4MB\n");
 		return (0);
 	}
-	
+
 	*tw = 0xAAAAAAAA;
-	
+
 	if (*tr != 0xAAAAAAAA) {
 		DPRINTK("EXIT - <4MB\n");
 		return (0);
 	}
-	
+
 	*tw = 0x55555555;
-	
+
 	if (*tr != 0x55555555) {
 		DPRINTK("EXIT - <4MB\n");
 		return (0);
 	}
-	
+
 	DPRINTK("EXIT\n");
 	return (1);
 }
 
 static void cv64_board_init (void)
 {
+	volatile unsigned char *regs = CyberRegs;
 	int i;
-	unsigned char test;
 	unsigned int clockpar;
+	unsigned char test;
 	
 	DPRINTK("ENTER\n");
 
@@ -1698,259 +1626,252 @@
 	 */
 	/* Reset board */
 	for (i = 0; i < 6; i++) {
-		cv64_write_port (0xff, (volatile unsigned char *) cv64_mem);
+		cv64_write_port (0xff, CyberBase);
 	}
 	/* Return to operational mode */
-	cv64_write_port (0x8004, (volatile unsigned char *) cv64_mem);
+	cv64_write_port (0x8004, CyberBase);
 	
-  /*
-   * Generic (?) S3 chip wakeup
-   */
-  /* Disable I/O & memory decoders, video in setup mode */
-  vgaw (cv64_regs, SREG_VIDEO_SUBS_ENABLE, 0x10);
-  /* Video responds to cmds, addrs & data */
-  vgaw (cv64_regs, SREG_OPTION_SELECT, 0x1);
-  /* Enable I/O & memory decoders, video in operational mode */
-  vgaw (cv64_regs, SREG_VIDEO_SUBS_ENABLE, 0x8);
-  /* VGA color emulation, enable cpu access to display mem */ 
-  vgaw (cv64_regs, GREG_MISC_OUTPUT_W, 0x03);
-  /* Unlock S3 VGA regs */
-  WCrt (cv64_regs, CRT_ID_REGISTER_LOCK_1, 0x48); 
-  /* Unlock system control & extension registers */
-  WCrt (cv64_regs, CRT_ID_REGISTER_LOCK_2, 0xA5);
+	/*
+	 * Generic (?) S3 chip wakeup
+	 */
+	/* Disable I/O & memory decoders, video in setup mode */
+	wb_64 (regs, SREG_VIDEO_SUBS_ENABLE, 0x10);
+	/* Video responds to cmds, addrs & data */
+	wb_64 (regs, SREG_OPTION_SELECT, 0x1);
+	/* Enable I/O & memory decoders, video in operational mode */
+	wb_64 (regs, SREG_VIDEO_SUBS_ENABLE, 0x8);
+	/* VGA color emulation, enable cpu access to display mem */ 
+	wb_64 (regs, GREG_MISC_OUTPUT_W, 0x03);
+	/* Unlock S3 VGA regs */
+	WCrt (regs, CRT_ID_REGISTER_LOCK_1, 0x48); 
+	/* Unlock system control & extension registers */
+	WCrt (regs, CRT_ID_REGISTER_LOCK_2, 0xA5);
 /* GRF - Enable interrupts */
-  /* Enable enhanced regs access, Ready cntl 0 wait states */
-  test = RCrt (cv64_regs, CRT_ID_SYSTEM_CONFIG);
-  test = test | 0x01;		/* enable enhaced register access */
-  test = test & 0xEF;		/* clear bit 4, 0 wait state */
-  WCrt (cv64_regs, CRT_ID_SYSTEM_CONFIG, test);
-  /*
-   * bit 0=1: Enable enhaced mode functions
-   * bit 2=0: Enhanced mode 8+ bits/pixel
-   * bit 4=1: Enable linear addressing
-   * bit 5=1: Enable MMIO
-   */
-  vgaw (cv64_regs, ECR_ADV_FUNC_CNTL, 0x31);
-  /*
-   * bit 0=1: Color emulation
-   * bit 1=1: Enable CPU access to display memory
-   * bit 5=1: Select high 64K memory page
-   */
+	/* Enable enhanced regs access, Ready cntl 0 wait states */
+	test = RCrt (regs, CRT_ID_SYSTEM_CONFIG);
+	test = test | 0x01;		/* enable enhanced register access */
+	test = test & 0xEF;		/* clear bit 4, 0 wait state */
+	WCrt (regs, CRT_ID_SYSTEM_CONFIG, test);
+	/*
+	 * bit 0=1: Enable enhaced mode functions
+	 * bit 2=0: Enhanced mode 8+ bits/pixel
+	 * bit 4=1: Enable linear addressing
+	 * bit 5=1: Enable MMIO
+	 */
+	wb_64 (regs, ECR_ADV_FUNC_CNTL, 0x31);
+	/*
+	 * bit 0=1: Color emulation
+	 * bit 1=1: Enable CPU access to display memory
+	 * bit 5=1: Select high 64K memory page
+	 */
 /* GRF - 0xE3 */
-  vgaw (cv64_regs, GREG_MISC_OUTPUT_W, 0x23);
+	wb_64 (regs, GREG_MISC_OUTPUT_W, 0x23);
 	
-  /* Cpu base addr */
-  WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_4, 0x0);
+	/* Cpu base addr */
+	WCrt (regs, CRT_ID_EXT_SYS_CNTL_4, 0x0);
 	
-  /* Reset. This does nothing on Trio, but standard VGA practice */
-  /* WSeq (cv64_regs, SEQ_ID_RESET, 0x03); */
-  /* Character clocks 8 dots wide */
-  WSeq (cv64_regs, SEQ_ID_CLOCKING_MODE, 0x01);
-  /* Enable cpu write to all color planes */
-  WSeq (cv64_regs, SEQ_ID_MAP_MASK, 0x0F);
-  /* Font table in 1st 8k of plane 2, font A=B disables swtich */
-  WSeq (cv64_regs, SEQ_ID_CHAR_MAP_SELECT, 0x0);
-  /* Allow mem access to 256kb */
-  WSeq (cv64_regs, SEQ_ID_MEMORY_MODE, 0x2);
-  /* Unlock S3 extensions to VGA Sequencer regs */
-  WSeq (cv64_regs, SEQ_ID_UNLOCK_EXT, 0x6);
-	
-  /* Enable 4MB fast page mode */
-  test = RSeq (cv64_regs, SEQ_ID_BUS_REQ_CNTL);
-  test = test | 1 << 6;
-  WSeq (cv64_regs, SEQ_ID_BUS_REQ_CNTL, test);
-	
-  /* Faster LUT write: 1 DCLK LUT write cycle, RAMDAC clk doubled */
-  WSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL, 0xC0);
-	
-  /* Clear immediate clock load bit */
-  test = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2);
-  test = test & 0xDF;
-  /* If > 55MHz, enable 2 cycle memory write */
-  if (cv64_memclk >= 55000000) {
-    test |= 0x80;
-  }
-  WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, test);
+	/* Reset. This does nothing on Trio, but standard VGA practice */
+	/* WSeq (CyberRegs, SEQ_ID_RESET, 0x03); */
+	/* Character clocks 8 dots wide */
+	WSeq (regs, SEQ_ID_CLOCKING_MODE, 0x01);
+	/* Enable cpu write to all color planes */
+	WSeq (regs, SEQ_ID_MAP_MASK, 0x0F);
+	/* Font table in 1st 8k of plane 2, font A=B disables swtich */
+	WSeq (regs, SEQ_ID_CHAR_MAP_SELECT, 0x0);
+	/* Allow mem access to 256kb */
+	WSeq (regs, SEQ_ID_MEMORY_MODE, 0x2);
+	/* Unlock S3 extensions to VGA Sequencer regs */
+	WSeq (regs, SEQ_ID_UNLOCK_EXT, 0x6);
+	
+	/* Enable 4MB fast page mode */
+	test = RSeq (regs, SEQ_ID_BUS_REQ_CNTL);
+	test = test | 1 << 6;
+	WSeq (regs, SEQ_ID_BUS_REQ_CNTL, test);
+	
+	/* Faster LUT write: 1 DCLK LUT write cycle, RAMDAC clk doubled */
+	WSeq (regs, SEQ_ID_RAMDAC_CNTL, 0xC0);
+
+	/* Clear immediate clock load bit */
+	test = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2);
+	test = test & 0xDF;
+	/* If > 55MHz, enable 2 cycle memory write */
+	if (cv64_memclk >= 55000000) {
+		test |= 0x80;
+	}
+	WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, test);
+
+	/* Set MCLK value */
+	clockpar = cv64_compute_clock (cv64_memclk);
+	test = (clockpar & 0xFF00) >> 8;
+	WSeq (regs, SEQ_ID_MCLK_HI, test);
+	test = clockpar & 0xFF;
+	WSeq (regs, SEQ_ID_MCLK_LO, test);
+
+	/* Chip rev specific: Not in my Trio manual!!! */
+	if (RCrt (regs, CRT_ID_REVISION) == 0x10)
+		WSeq (regs, SEQ_ID_MORE_MAGIC, test);
+
+	/* We now load an 25 MHz, 31kHz, 640x480 standard VGA Mode. */
+
+	/* Set DCLK value */
+	WSeq (regs, SEQ_ID_DCLK_HI, 0x13);
+	WSeq (regs, SEQ_ID_DCLK_LO, 0x41);
+
+	/* Load DCLK (and MCLK?) immediately */
+	test = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2);
+	test = test | 0x22;
+	WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, test);
+
+	/* Enable loading of DCLK */
+	test = rb_64(regs, GREG_MISC_OUTPUT_R);
+	test = test | 0x0C;
+	wb_64 (regs, GREG_MISC_OUTPUT_W, test);
+
+	/* Turn off immediate xCLK load */
+	WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, 0x2);
+
+	/* Horizontal character clock counts */
+	/* 8 LSB of 9 bits = total line - 5 */
+	WCrt (regs, CRT_ID_HOR_TOTAL, 0x5F);
+	/* Active display line */
+	WCrt (regs, CRT_ID_HOR_DISP_ENA_END, 0x4F);
+	/* Blank assertion start */
+	WCrt (regs, CRT_ID_START_HOR_BLANK, 0x50);
+	/* Blank assertion end */
+	WCrt (regs, CRT_ID_END_HOR_BLANK, 0x82);
+	/* HSYNC assertion start */
+	WCrt (regs, CRT_ID_START_HOR_RETR, 0x54);
+	/* HSYNC assertion end */
+	WCrt (regs, CRT_ID_END_HOR_RETR, 0x80);
+	WCrt (regs, CRT_ID_VER_TOTAL, 0xBF);
+	WCrt (regs, CRT_ID_OVERFLOW, 0x1F);
+	WCrt (regs, CRT_ID_PRESET_ROW_SCAN, 0x0);
+	WCrt (regs, CRT_ID_MAX_SCAN_LINE, 0x40);
+	WCrt (regs, CRT_ID_CURSOR_START, 0x00);
+	WCrt (regs, CRT_ID_CURSOR_END, 0x00);
+	WCrt (regs, CRT_ID_START_ADDR_HIGH, 0x00);
+	WCrt (regs, CRT_ID_START_ADDR_LOW, 0x00);
+	WCrt (regs, CRT_ID_CURSOR_LOC_HIGH, 0x00);
+	WCrt (regs, CRT_ID_CURSOR_LOC_LOW, 0x00);
+	WCrt (regs, CRT_ID_START_VER_RETR, 0x9C);
+	WCrt (regs, CRT_ID_END_VER_RETR, 0x0E);
+	WCrt (regs, CRT_ID_VER_DISP_ENA_END, 0x8F);
+	WCrt (regs, CRT_ID_SCREEN_OFFSET, 0x50);
+	WCrt (regs, CRT_ID_UNDERLINE_LOC, 0x00);
+	WCrt (regs, CRT_ID_START_VER_BLANK, 0x96);
+	WCrt (regs, CRT_ID_END_VER_BLANK, 0xB9);
+	WCrt (regs, CRT_ID_MODE_CONTROL, 0xE3);
+	WCrt (regs, CRT_ID_LINE_COMPARE, 0xFF);
+	WCrt (regs, CRT_ID_BACKWAD_COMP_3, 0x10);	/* FIFO enabled */
+	WCrt (regs, CRT_ID_MISC_1, 0x35);
+	WCrt (regs, CRT_ID_DISPLAY_FIFO, 0x5A);
+	WCrt (regs, CRT_ID_EXT_MEM_CNTL_2, 0x70);
+	WCrt (regs, CRT_ID_LAW_POS_LO, 0x40);
+	WCrt (regs, CRT_ID_EXT_MEM_CNTL_3, 0xFF);
+
+	WGfx (regs, GCT_ID_SET_RESET, 0x0);
+	WGfx (regs, GCT_ID_ENABLE_SET_RESET, 0x0);
+	WGfx (regs, GCT_ID_COLOR_COMPARE, 0x0);
+	WGfx (regs, GCT_ID_DATA_ROTATE, 0x0);
+	WGfx (regs, GCT_ID_READ_MAP_SELECT, 0x0);
+	WGfx (regs, GCT_ID_GRAPHICS_MODE, 0x40);
+	WGfx (regs, GCT_ID_MISC, 0x01);
+	WGfx (regs, GCT_ID_COLOR_XCARE, 0x0F);
+	WGfx (regs, GCT_ID_BITMASK, 0xFF);
+
+	/* Colors for text mode */
+	for (i = 0; i < 0xf; i++)
+		WAttr (regs, i, i);
+
+	WAttr (regs, ACT_ID_ATTR_MODE_CNTL, 0x41);
+	WAttr (regs, ACT_ID_OVERSCAN_COLOR, 0x01);
+	WAttr (regs, ACT_ID_COLOR_PLANE_ENA, 0x0F);
+	WAttr (regs, ACT_ID_HOR_PEL_PANNING, 0x0);
+	WAttr (regs, ACT_ID_COLOR_SELECT, 0x0);
+
+	wb_64 (regs, VDAC_MASK, 0xFF);
+
+	*((unsigned long *) (regs + ECR_FRGD_COLOR)) = 0xFF;
+	*((unsigned long *) (regs + ECR_BKGD_COLOR)) = 0;
+
+	/* Colors initially set to grayscale */
+
+	wb_64 (regs, VDAC_ADDRESS_W, 0);
+	for (i = 255; i >= 0; i--) {
+		wb_64(regs, VDAC_DATA, i);
+		wb_64(regs, VDAC_DATA, i);
+		wb_64(regs, VDAC_DATA, i);
+	}
+
+	/* GFx hardware cursor off */
+	WCrt (regs, CRT_ID_HWGC_MODE, 0x00);
+
+	/* Set first to 4MB, so test will work */
+	WCrt (regs, CRT_ID_LAW_CNTL, 0x13);
+	/* Find "correct" size of fbmem of Z3 board */
+	if (cv_has_4mb (CyberMem)) {
+		CyberSize = 1024 * 1024 * 4;
+		WCrt (regs, CRT_ID_LAW_CNTL, 0x13);
+		DPRINTK("4MB board\n");
+	} else {
+		CyberSize = 1024 * 1024 * 2;
+		WCrt (regs, CRT_ID_LAW_CNTL, 0x12);
+		DPRINTK("2MB board\n");
+	}
 
-  /* Set MCLK value */
-  clockpar = cv64_compute_clock (cv64_memclk);
-  test = (clockpar & 0xFF00) >> 8;
-  WSeq (cv64_regs, SEQ_ID_MCLK_HI, test);
-  test = clockpar & 0xFF;
-  WSeq (cv64_regs, SEQ_ID_MCLK_LO, test);
-
-  /* Chip rev specific: Not in my Trio manual!!! */
-  if (RCrt (cv64_regs, CRT_ID_REVISION) == 0x10)
-    WSeq (cv64_regs, SEQ_ID_MORE_MAGIC, test);
-	
-  /* We now load an 25 MHz, 31kHz, 640x480 standard VGA Mode. */
-
-  /* Set DCLK value */
-  WSeq (cv64_regs, SEQ_ID_DCLK_HI, 0x13);
-  WSeq (cv64_regs, SEQ_ID_DCLK_LO, 0x41);
-
-  /* Load DCLK (and MCLK?) immediately */
-  test = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2);
-  test = test | 0x22;
-  WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, test);
-
-  /* Enable loading of DCLK */
-  test = vgar (cv64_regs, GREG_MISC_OUTPUT_R);
-  test = test | 0x0C;
-  vgaw (cv64_regs, GREG_MISC_OUTPUT_W, test);
-
-  /* Turn off immediate xCLK load */
-  WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, 0x2);
-
-  /* Horizontal character clock counts */
-  /* 8 LSB of 9 bits = total line - 5 */
-  WCrt (cv64_regs, CRT_ID_HOR_TOTAL, 0x5F);
-  /* Active display line */
-  WCrt (cv64_regs, CRT_ID_HOR_DISP_ENA_END, 0x4F);
-  /* Blank assertion start */
-  WCrt (cv64_regs, CRT_ID_START_HOR_BLANK, 0x50);
-  /* Blank assertion end */
-  WCrt (cv64_regs, CRT_ID_END_HOR_BLANK, 0x82);
-  /* HSYNC assertion start */
-  WCrt (cv64_regs, CRT_ID_START_HOR_RETR, 0x54);
-  /* HSYNC assertion end */
-  WCrt (cv64_regs, CRT_ID_END_HOR_RETR, 0x80);
-  WCrt (cv64_regs, CRT_ID_VER_TOTAL, 0xBF);
-  WCrt (cv64_regs, CRT_ID_OVERFLOW, 0x1F);
-  WCrt (cv64_regs, CRT_ID_PRESET_ROW_SCAN, 0x0);
-  WCrt (cv64_regs, CRT_ID_MAX_SCAN_LINE, 0x40);
-  WCrt (cv64_regs, CRT_ID_CURSOR_START, 0x00);
-  WCrt (cv64_regs, CRT_ID_CURSOR_END, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_ADDR_HIGH, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_ADDR_LOW, 0x00);
-  WCrt (cv64_regs, CRT_ID_CURSOR_LOC_HIGH, 0x00);
-  WCrt (cv64_regs, CRT_ID_CURSOR_LOC_LOW, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_VER_RETR, 0x9C);
-  WCrt (cv64_regs, CRT_ID_END_VER_RETR, 0x0E);
-  WCrt (cv64_regs, CRT_ID_VER_DISP_ENA_END, 0x8F);
-  WCrt (cv64_regs, CRT_ID_SCREEN_OFFSET, 0x50);
-  WCrt (cv64_regs, CRT_ID_UNDERLINE_LOC, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_VER_BLANK, 0x96);
-  WCrt (cv64_regs, CRT_ID_END_VER_BLANK, 0xB9);
-  WCrt (cv64_regs, CRT_ID_MODE_CONTROL, 0xE3);
-  WCrt (cv64_regs, CRT_ID_LINE_COMPARE, 0xFF);
-  WCrt (cv64_regs, CRT_ID_BACKWAD_COMP_3, 0x10);	/* FIFO enabled */
-  WCrt (cv64_regs, CRT_ID_MISC_1, 0x35);
-  WCrt (cv64_regs, CRT_ID_DISPLAY_FIFO, 0x5A);
-  WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_2, 0x70);
-  WCrt (cv64_regs, CRT_ID_LAW_POS_LO, 0x40);
-  WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_3, 0xFF);
-  
-  WGfx (cv64_regs, GCT_ID_SET_RESET, 0x0);
-  WGfx (cv64_regs, GCT_ID_ENABLE_SET_RESET, 0x0);
-  WGfx (cv64_regs, GCT_ID_COLOR_COMPARE, 0x0);
-  WGfx (cv64_regs, GCT_ID_DATA_ROTATE, 0x0);
-  WGfx (cv64_regs, GCT_ID_READ_MAP_SELECT, 0x0);
-  WGfx (cv64_regs, GCT_ID_GRAPHICS_MODE, 0x40);
-  WGfx (cv64_regs, GCT_ID_MISC, 0x01);
-  WGfx (cv64_regs, GCT_ID_COLOR_XCARE, 0x0F);
-  WGfx (cv64_regs, GCT_ID_BITMASK, 0xFF);
-	
-  /* Colors for text mode */
-  for (i = 0; i < 0xf; i++)
-    WAttr (cv64_regs, i, i);
-	
-  WAttr (cv64_regs, ACT_ID_ATTR_MODE_CNTL, 0x41);
-  WAttr (cv64_regs, ACT_ID_OVERSCAN_COLOR, 0x01);
-  WAttr (cv64_regs, ACT_ID_COLOR_PLANE_ENA, 0x0F);
-  WAttr (cv64_regs, ACT_ID_HOR_PEL_PANNING, 0x0);
-  WAttr (cv64_regs, ACT_ID_COLOR_SELECT, 0x0);
-	
-  vgaw (cv64_regs, VDAC_MASK, 0xFF);
-	
-  *((unsigned long *) (cv64_regs + ECR_FRGD_COLOR)) = 0xFF;
-  *((unsigned long *) (cv64_regs + ECR_BKGD_COLOR)) = 0;
-	
-  /* Colors initially set to grayscale */
-	
-  vgaw (cv64_regs, VDAC_ADDRESS_W, 0);
-  for (i = 255; i >= 0; i--) {
-    vgaw (cv64_regs, VDAC_DATA, i);
-    vgaw (cv64_regs, VDAC_DATA, i);
-    vgaw (cv64_regs, VDAC_DATA, i);
-  }
+	/* Initialize graphics engine */
+	Cyber_WaitBlit();
+	vgaw16 (regs, ECR_FRGD_MIX, 0x27);
+	vgaw16 (regs, ECR_BKGD_MIX, 0x07);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0x1000);
+	udelay(200);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0x2000);
+	Cyber_WaitBlit();
+	vgaw16 (regs, ECR_READ_REG_DATA, 0x3FFF);
+	Cyber_WaitBlit();
+	udelay(200);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0x4FFF);
+	Cyber_WaitBlit();
+	vgaw16 (regs, ECR_BITPLANE_WRITE_MASK, ~0);
+	Cyber_WaitBlit();
+	vgaw16 (regs, ECR_READ_REG_DATA, 0xE000);
+	vgaw16 (regs, ECR_CURRENT_Y_POS2, 0x00);
+	vgaw16 (regs, ECR_CURRENT_X_POS2, 0x00);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0xA000);
+	vgaw16 (regs, ECR_DEST_Y__AX_STEP, 0x00);
+	vgaw16 (regs, ECR_DEST_Y2__AX_STEP2, 0x00);
+	vgaw16 (regs, ECR_DEST_X__DIA_STEP, 0x00);
+	vgaw16 (regs, ECR_DEST_X2__DIA_STEP2, 0x00);
+	vgaw16 (regs, ECR_SHORT_STROKE, 0x00);
+	vgaw16 (regs, ECR_DRAW_CMD, 0x01);
 
-  /* GFx hardware cursor off */
-  WCrt (cv64_regs, CRT_ID_HWGC_MODE, 0x00);
+	Cyber_WaitBlit();
 
-  /* Set first to 4MB, so test will work */
-  WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x13);
-  /* Find "correct" size of fbmem of Z3 board */
-  if (cv_has_4mb ((volatile caddr_t) cv64_fbmem)) {
-    cv64_size = 1024 * 1024 * 4;
-    WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x13);
-    DPRINTK("4MB board\n");
-  } else {
-    cv64_size = 1024 * 1024 * 2;
-    WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x12);
-    DPRINTK("2MB board\n");
-  }
-	
-  /* Initialize graphics engine */
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-  vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x27);
-  vgaw16 (cv64_regs, ECR_BKGD_MIX, 0x07);
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x1000);
-  udelay(200);
-  /* __cv_delay (200000); */
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x2000);
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x3FFF);
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-  udelay(200);
-  /* __cv_delay (200000); */
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x4FFF);
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-  vgaw16 (cv64_regs, ECR_BITPLANE_WRITE_MASK, ~0);
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xE000);
-  vgaw16 (cv64_regs, ECR_CURRENT_Y_POS2, 0x00);
-  vgaw16 (cv64_regs, ECR_CURRENT_X_POS2, 0x00);
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000);
-  vgaw16 (cv64_regs, ECR_DEST_Y__AX_STEP, 0x00);
-  vgaw16 (cv64_regs, ECR_DEST_Y2__AX_STEP2, 0x00);
-  vgaw16 (cv64_regs, ECR_DEST_X__DIA_STEP, 0x00);
-  vgaw16 (cv64_regs, ECR_DEST_X2__DIA_STEP2, 0x00);
-  vgaw16 (cv64_regs, ECR_SHORT_STROKE, 0x00);
-  vgaw16 (cv64_regs, ECR_DRAW_CMD, 0x01);
-	
-  Cyber_WaitBlit();
-  /* GfxBusyWait (cv64_regs); */
-	
-  vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x4FFF);
-  vgaw16 (cv64_regs, ECR_BKGD_COLOR, 0x01);
-  vgaw16 (cv64_regs, ECR_FRGD_COLOR, 0x00);
-	
-	
-  /* Enable video display (set bit 5) */
+	vgaw16 (regs, ECR_READ_REG_DATA, 0x4FFF);
+	vgaw16 (regs, ECR_BKGD_COLOR, 0x01);
+	vgaw16 (regs, ECR_FRGD_COLOR, 0x00);
+
+
+	/* Enable video display (set bit 5) */
 /* ARB - Would also seem to write to AR13.
  *       May want to use parts of WAttr to set JUST bit 5
  */
-  WAttr (cv64_regs, 0x33, 0);
+	WAttr (regs, 0x33, 0);
 	
 /* GRF - function code ended here */
 
-  /* Turn gfx on again */
-  gfx_on_off (0, cv64_regs);
-	
-  /* Pass-through */
-  cvscreen (0, (volatile unsigned char *) cv64_mem);
+	/* Turn gfx on again */
+	gfx_on_off (0, regs);
 
-  DPRINTK("EXIT\n");
+	/* Pass-through */
+	cvscreen (0, CyberBase);
+
+	DPRINTK("EXIT\n");
 }
 
 static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode)
 {
+  volatile unsigned char *regs = CyberRegs;
   int fx, fy;
   unsigned short mnr;
   unsigned short HT, HDE, HBS, HBE, HSS, HSE, VDE, VBS, VBE, VSS, VSE, VT;
@@ -1977,7 +1898,7 @@
 
 /* GRF - Disable interrupts */	
 	
-  gfx_on_off (1, cv64_regs);
+  gfx_on_off (1, regs);
 	
   switch (video_mode->bits_per_pixel) {
   case 15:
@@ -2049,7 +1970,7 @@
     VT  = yres + vfront + vsync + vback - 2;
   }
 
-  vgaw (cv64_regs, ECR_ADV_FUNC_CNTL, (TEXT ? 0x00 : 0x31));
+  wb_64 (regs, ECR_ADV_FUNC_CNTL, (TEXT ? 0x00 : 0x31));
 	
   if (TEXT)
     HDE = ((video_mode->xres + fx - 1) / fx) - 1;
@@ -2058,15 +1979,15 @@
 	
   VDE = video_mode->yres - 1;
 
-  WCrt (cv64_regs, CRT_ID_HWGC_MODE, 0x00);
-  WCrt (cv64_regs, CRT_ID_EXT_DAC_CNTL, 0x00);
+  WCrt (regs, CRT_ID_HWGC_MODE, 0x00);
+  WCrt (regs, CRT_ID_EXT_DAC_CNTL, 0x00);
 	
-  WSeq (cv64_regs, SEQ_ID_MEMORY_MODE,
+  WSeq (regs, SEQ_ID_MEMORY_MODE,
 	(TEXT || (video_mode->bits_per_pixel == 1)) ? 0x06 : 0x0e);
-  WGfx (cv64_regs, GCT_ID_READ_MAP_SELECT, 0x00);
-  WSeq (cv64_regs, SEQ_ID_MAP_MASK,
+  WGfx (regs, GCT_ID_READ_MAP_SELECT, 0x00);
+  WSeq (regs, SEQ_ID_MAP_MASK,
 	(video_mode->bits_per_pixel == 1) ? 0x01 : 0xFF);
-  WSeq (cv64_regs, SEQ_ID_CHAR_MAP_SELECT, 0x00);
+  WSeq (regs, SEQ_ID_CHAR_MAP_SELECT, 0x00);
 	
   /* cv64_compute_clock accepts arguments in Hz */
   /* pixclock is in ps ... convert to Hz */
@@ -2081,11 +2002,11 @@
 #endif
 
   mnr = cv64_compute_clock (freq);
-  WSeq (cv64_regs, SEQ_ID_DCLK_HI, ((mnr & 0xFF00) >> 8));
-  WSeq (cv64_regs, SEQ_ID_DCLK_LO, (mnr & 0xFF));
+  WSeq (regs, SEQ_ID_DCLK_HI, ((mnr & 0xFF00) >> 8));
+  WSeq (regs, SEQ_ID_DCLK_LO, (mnr & 0xFF));
 	
   /* Load display parameters into board */
-  WCrt (cv64_regs, CRT_ID_EXT_HOR_OVF,
+  WCrt (regs, CRT_ID_EXT_HOR_OVF,
 	((HT & 0x100) ? 0x01 : 0x00) |
 	((HDE & 0x100) ? 0x02 : 0x00) |
 	((HBS & 0x100) ? 0x04 : 0x00) |
@@ -2095,7 +2016,7 @@
 	(((HT-5) & 0x100) ? 0x40 : 0x00)
 	);
 	
-  WCrt (cv64_regs, CRT_ID_EXT_VER_OVF,
+  WCrt (regs, CRT_ID_EXT_VER_OVF,
 	0x40 |
 	((VT & 0x400) ? 0x01 : 0x00) |
 	((VDE & 0x400) ? 0x02 : 0x00) |
@@ -2103,18 +2024,18 @@
 	((VSS & 0x400) ? 0x10 : 0x00)
 	);
 	
-  WCrt (cv64_regs, CRT_ID_HOR_TOTAL, HT);
-  WCrt (cv64_regs, CRT_ID_DISPLAY_FIFO, HT - 5);
-  WCrt (cv64_regs, CRT_ID_HOR_DISP_ENA_END, ((HDE >= HBS) ? (HBS - 1) : HDE));
-  WCrt (cv64_regs, CRT_ID_START_HOR_BLANK, HBS);
-  WCrt (cv64_regs, CRT_ID_END_HOR_BLANK, ((HBE & 0x1F) | 0x80));
-  WCrt (cv64_regs, CRT_ID_START_HOR_RETR, HSS);
-  WCrt (cv64_regs, CRT_ID_END_HOR_RETR,
+  WCrt (regs, CRT_ID_HOR_TOTAL, HT);
+  WCrt (regs, CRT_ID_DISPLAY_FIFO, HT - 5);
+  WCrt (regs, CRT_ID_HOR_DISP_ENA_END, ((HDE >= HBS) ? (HBS - 1) : HDE));
+  WCrt (regs, CRT_ID_START_HOR_BLANK, HBS);
+  WCrt (regs, CRT_ID_END_HOR_BLANK, ((HBE & 0x1F) | 0x80));
+  WCrt (regs, CRT_ID_START_HOR_RETR, HSS);
+  WCrt (regs, CRT_ID_END_HOR_RETR,
 	(HSE & 0x1F) |
 	((HBE & 0x20) ? 0x80 : 0x00)
 	);
-  WCrt (cv64_regs, CRT_ID_VER_TOTAL, VT);
-  WCrt (cv64_regs, CRT_ID_OVERFLOW,
+  WCrt (regs, CRT_ID_VER_TOTAL, VT);
+  WCrt (regs, CRT_ID_OVERFLOW,
 	0x10 |
 	((VT & 0x100) ? 0x01 : 0x00) |
 	((VDE & 0x100) ? 0x02 : 0x00) |
@@ -2124,65 +2045,65 @@
 	((VDE & 0x200) ? 0x40 : 0x00) |
 	((VSS & 0x200) ? 0x80 : 0x00)
 	);
-  WCrt (cv64_regs, CRT_ID_MAX_SCAN_LINE,
+  WCrt (regs, CRT_ID_MAX_SCAN_LINE,
 	0x40 |
 	(DBLSCAN ? 0x80 : 0x00) |
 	((VBS & 0x200) ? 0x20 : 0x00) |
 	(TEXT ? ((fy - 1) & 0x1F) : 0x00)
 	);
 	
-  WCrt (cv64_regs, CRT_ID_MODE_CONTROL, 0xE3);
+  WCrt (regs, CRT_ID_MODE_CONTROL, 0xE3);
 
   /* Text cursor */
 	
   if (TEXT) {
 #if 1
-    WCrt (cv64_regs, CRT_ID_CURSOR_START, (fy & 0x1f) - 2);
-    WCrt (cv64_regs, CRT_ID_CURSOR_END, (fy & 0x1F) - 1);
+    WCrt (regs, CRT_ID_CURSOR_START, (fy & 0x1f) - 2);
+    WCrt (regs, CRT_ID_CURSOR_END, (fy & 0x1F) - 1);
 #else
-    WCrt (cv64_regs, CRT_ID_CURSOR_START, 0x00);
-    WCrt (cv64_regs, CRT_ID_CURSOR_END, fy & 0x1F);
+    WCrt (regs, CRT_ID_CURSOR_START, 0x00);
+    WCrt (regs, CRT_ID_CURSOR_END, fy & 0x1F);
 #endif
-    WCrt (cv64_regs, CRT_ID_UNDERLINE_LOC, (fy - 1) & 0x1F);
-    WCrt (cv64_regs, CRT_ID_CURSOR_LOC_HIGH, 0x00);
-    WCrt (cv64_regs, CRT_ID_CURSOR_LOC_LOW, 0x00);
+    WCrt (regs, CRT_ID_UNDERLINE_LOC, (fy - 1) & 0x1F);
+    WCrt (regs, CRT_ID_CURSOR_LOC_HIGH, 0x00);
+    WCrt (regs, CRT_ID_CURSOR_LOC_LOW, 0x00);
   }
 	
-  WCrt (cv64_regs, CRT_ID_START_ADDR_HIGH, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_ADDR_LOW, 0x00);
-  WCrt (cv64_regs, CRT_ID_START_VER_RETR, VSS);
-  WCrt (cv64_regs, CRT_ID_END_VER_RETR, (VSE & 0x0F));
-  WCrt (cv64_regs, CRT_ID_VER_DISP_ENA_END, VDE);
-  WCrt (cv64_regs, CRT_ID_START_VER_BLANK, VBS);
-  WCrt (cv64_regs, CRT_ID_END_VER_BLANK, VBE);
-  WCrt (cv64_regs, CRT_ID_LINE_COMPARE, 0xFF);
-  WCrt (cv64_regs, CRT_ID_LACE_RETR_START, HT / 2);
-  WCrt (cv64_regs, CRT_ID_LACE_CONTROL, (LACE ? 0x20 : 0x00));
-  WGfx (cv64_regs, GCT_ID_GRAPHICS_MODE,
+  WCrt (regs, CRT_ID_START_ADDR_HIGH, 0x00);
+  WCrt (regs, CRT_ID_START_ADDR_LOW, 0x00);
+  WCrt (regs, CRT_ID_START_VER_RETR, VSS);
+  WCrt (regs, CRT_ID_END_VER_RETR, (VSE & 0x0F));
+  WCrt (regs, CRT_ID_VER_DISP_ENA_END, VDE);
+  WCrt (regs, CRT_ID_START_VER_BLANK, VBS);
+  WCrt (regs, CRT_ID_END_VER_BLANK, VBE);
+  WCrt (regs, CRT_ID_LINE_COMPARE, 0xFF);
+  WCrt (regs, CRT_ID_LACE_RETR_START, HT / 2);
+  WCrt (regs, CRT_ID_LACE_CONTROL, (LACE ? 0x20 : 0x00));
+  WGfx (regs, GCT_ID_GRAPHICS_MODE,
 	((TEXT || (video_mode->bits_per_pixel == 1)) ? 0x00 : 0x40));
-  WGfx (cv64_regs, GCT_ID_MISC, (TEXT ? 0x04 : 0x01));
-  WSeq (cv64_regs, SEQ_ID_MEMORY_MODE,
+  WGfx (regs, GCT_ID_MISC, (TEXT ? 0x04 : 0x01));
+  WSeq (regs, SEQ_ID_MEMORY_MODE,
 	((TEXT || (video_mode->bits_per_pixel == 1)) ? 0x06 : 0x02));
 	
-  vgaw (cv64_regs, VDAC_MASK, 0xFF);
+  wb_64 (regs, VDAC_MASK, 0xFF);
 	
   /* Blank border */
-  test = RCrt (cv64_regs, CRT_ID_BACKWAD_COMP_2);
-  WCrt (cv64_regs, CRT_ID_BACKWAD_COMP_2, (test | 0x20));
+  test = RCrt (regs, CRT_ID_BACKWAD_COMP_2);
+  WCrt (regs, CRT_ID_BACKWAD_COMP_2, (test | 0x20));
 	
-  sr15 = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2);
+  sr15 = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2);
   sr15 &= 0xEF;
-  sr18 = RSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL);
+  sr18 = RSeq (regs, SEQ_ID_RAMDAC_CNTL);
   sr18 &= 0x7F;
   clock_mode = 0x00;
   cr50 = 0x00;
 	
-  test = RCrt (cv64_regs, CRT_ID_EXT_MISC_CNTL_2);
+  test = RCrt (regs, CRT_ID_EXT_MISC_CNTL_2);
   test &= 0xD;
 	
   /* Clear roxxler byte-swapping... */
-  cv64_write_port (0x0040, (volatile unsigned char *) cv64_mem);
-  cv64_write_port (0x0020, (volatile unsigned char *) cv64_mem);
+  cv64_write_port (0x0040, CyberBase);
+  cv64_write_port (0x0020, CyberBase);
 	
   switch (video_mode->bits_per_pixel) {
   case 1:
@@ -2201,14 +2122,14 @@
     break;
 		
   case 15:
-    cv64_write_port (0x8020, (volatile unsigned char *) cv64_mem);
+    cv64_write_port (0x8020, CyberBase);
     clock_mode = 0x30;
     HDE = video_mode->xres / 4;
     cr50 |= 0x10;
     break;
 		
   case 16:
-    cv64_write_port (0x8020, (volatile unsigned char *) cv64_mem);
+    cv64_write_port (0x8020, CyberBase);
     clock_mode = 0x50;
     HDE = video_mode->xres / 4;
     cr50 |= 0x10;
@@ -2216,24 +2137,24 @@
 		
   case 24:
   case 32:
-    cv64_write_port (0x8040, (volatile unsigned char *) cv64_mem);
+    cv64_write_port (0x8040, CyberBase);
     clock_mode = 0xD0;
     HDE = video_mode->xres / 2;
     cr50 |= 0x30;
     break;
   }
-	
-  WCrt (cv64_regs, CRT_ID_EXT_MISC_CNTL_2, clock_mode | test);
-  WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, sr15);
-  WSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL, sr18);
-  WCrt (cv64_regs, CRT_ID_SCREEN_OFFSET, HDE);
 
-  WCrt (cv64_regs, CRT_ID_MISC_1, (TEXT ? 0x05 : 0x35));
+  WCrt (regs, CRT_ID_EXT_MISC_CNTL_2, clock_mode | test);
+  WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, sr15);
+  WSeq (regs, SEQ_ID_RAMDAC_CNTL, sr18);
+  WCrt (regs, CRT_ID_SCREEN_OFFSET, HDE);
+
+  WCrt (regs, CRT_ID_MISC_1, (TEXT ? 0x05 : 0x35));
 	
-  test = RCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_2);
+  test = RCrt (regs, CRT_ID_EXT_SYS_CNTL_2);
   test &= ~0x30;
   test |= (HDE >> 4) & 0x30;
-  WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_2, test);
+  WCrt (regs, CRT_ID_EXT_SYS_CNTL_2, test);
 	
   /* Set up graphics engine */
   switch (video_mode->xres) {
@@ -2265,17 +2186,14 @@
     break;
   }
 	
-  WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_1, cr50);
+  WCrt (regs, CRT_ID_EXT_SYS_CNTL_1, cr50);
 	
   udelay(100);
-  /* __cv_delay (100000); */
-  WAttr (cv64_regs, ACT_ID_ATTR_MODE_CNTL, (TEXT ? 0x08 : 0x41));
+  WAttr (regs, ACT_ID_ATTR_MODE_CNTL, (TEXT ? 0x08 : 0x41));
   udelay(100);
-  /* __cv_delay (100000); */
-  WAttr (cv64_regs, ACT_ID_COLOR_PLANE_ENA,
+  WAttr (regs, ACT_ID_COLOR_PLANE_ENA,
 	 (video_mode->bits_per_pixel == 1) ? 0x01 : 0x0F);
   udelay(100);
-  /* __cv_delay (100000); */
 	
   tfillm = (96 * (cv64_memclk / 1000)) / 240000;
 	
@@ -2304,10 +2222,9 @@
     m = 0x18;
   n = 0xFF;
 	
-  WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_2, m);
-  WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_3, n);
+  WCrt (regs, CRT_ID_EXT_MEM_CNTL_2, m);
+  WCrt (regs, CRT_ID_EXT_MEM_CNTL_3, n);
   udelay(10);
-  /* __cv_delay (10000); */
 	
   /* Text initialization */
 	
@@ -2317,21 +2234,21 @@
 	
   if (CONSOLE) {
     int i;
-    vgaw (cv64_regs, VDAC_ADDRESS_W, 0);
+    wb_64 (regs, VDAC_ADDRESS_W, 0);
     for (i = 0; i < 4; i++) {
-      vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][0]);
-      vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][1]);
-      vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][2]);
+      wb_64 (regs, VDAC_DATA, cvconscolors [i][0]);
+      wb_64 (regs, VDAC_DATA, cvconscolors [i][1]);
+      wb_64 (regs, VDAC_DATA, cvconscolors [i][2]);
     }
   }
 	
-  WAttr (cv64_regs, 0x33, 0);
+  WAttr (regs, 0x33, 0);
 	
   /* Turn gfx on again */
-  gfx_on_off (0, (volatile unsigned char *) cv64_regs);
+  gfx_on_off (0, (volatile unsigned char *) regs);
 	
   /* Pass-through */
-  cvscreen (0, (volatile unsigned char *) cv64_mem);
+  cvscreen (0, CyberBase);
 
 DPRINTK("EXIT\n");
 }
@@ -2339,6 +2256,7 @@
 void cvision_bitblt (u_short sx, u_short sy, u_short dx, u_short dy,
 		     u_short w, u_short h)
 {
+	volatile unsigned char *regs = CyberRegs;
 	unsigned short drawdir = 0;
 	
 	DPRINTK("ENTER\n");
@@ -2357,37 +2275,36 @@
 	}
 	
 	Cyber_WaitBlit();
-	/* GfxBusyWait (cv64_regs); */
-	vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000);
-	vgaw16 (cv64_regs, ECR_BKGD_MIX, 0x7);
-	vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x67);
-	vgaw16 (cv64_regs, ECR_BKGD_COLOR, 0x0);
-	vgaw16 (cv64_regs, ECR_FRGD_COLOR, 0x1);
-	vgaw16 (cv64_regs, ECR_BITPLANE_READ_MASK, 0x1);
-	vgaw16 (cv64_regs, ECR_BITPLANE_WRITE_MASK, 0xFFF);
-	vgaw16 (cv64_regs, ECR_CURRENT_Y_POS, sy);
-	vgaw16 (cv64_regs, ECR_CURRENT_X_POS, sx);
-	vgaw16 (cv64_regs, ECR_DEST_Y__AX_STEP, dy);
-	vgaw16 (cv64_regs, ECR_DEST_X__DIA_STEP, dx);
-	vgaw16 (cv64_regs, ECR_READ_REG_DATA, h - 1);
-	vgaw16 (cv64_regs, ECR_MAJ_AXIS_PIX_CNT, w - 1);
-	vgaw16 (cv64_regs, ECR_DRAW_CMD, 0xC051 | drawdir);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0xA000);
+	vgaw16 (regs, ECR_BKGD_MIX, 0x7);
+	vgaw16 (regs, ECR_FRGD_MIX, 0x67);
+	vgaw16 (regs, ECR_BKGD_COLOR, 0x0);
+	vgaw16 (regs, ECR_FRGD_COLOR, 0x1);
+	vgaw16 (regs, ECR_BITPLANE_READ_MASK, 0x1);
+	vgaw16 (regs, ECR_BITPLANE_WRITE_MASK, 0xFFF);
+	vgaw16 (regs, ECR_CURRENT_Y_POS, sy);
+	vgaw16 (regs, ECR_CURRENT_X_POS, sx);
+	vgaw16 (regs, ECR_DEST_Y__AX_STEP, dy);
+	vgaw16 (regs, ECR_DEST_X__DIA_STEP, dx);
+	vgaw16 (regs, ECR_READ_REG_DATA, h - 1);
+	vgaw16 (regs, ECR_MAJ_AXIS_PIX_CNT, w - 1);
+	vgaw16 (regs, ECR_DRAW_CMD, 0xC051 | drawdir);
 	DPRINTK("EXIT\n");
 }
 
 void cvision_clear (u_short dx, u_short dy, u_short w, u_short h, u_short bg)
 {
+	volatile unsigned char *regs = CyberRegs;
 	DPRINTK("ENTER\n");
 	Cyber_WaitBlit();
-	/* GfxBusyWait (cv64_regs); */
-	vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x0027);
-	vgaw16 (cv64_regs, ECR_FRGD_COLOR, bg);
-	vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000);
-	vgaw16 (cv64_regs, ECR_CURRENT_Y_POS, dy);
-	vgaw16 (cv64_regs, ECR_CURRENT_X_POS, dx);
-	vgaw16 (cv64_regs, ECR_READ_REG_DATA, h - 1);
-	vgaw16 (cv64_regs, ECR_MAJ_AXIS_PIX_CNT, w - 1);
-	vgaw16 (cv64_regs, ECR_DRAW_CMD, 0x40B1);	
+	vgaw16 (regs, ECR_FRGD_MIX, 0x0027);
+	vgaw16 (regs, ECR_FRGD_COLOR, bg);
+	vgaw16 (regs, ECR_READ_REG_DATA, 0xA000);
+	vgaw16 (regs, ECR_CURRENT_Y_POS, dy);
+	vgaw16 (regs, ECR_CURRENT_X_POS, dx);
+	vgaw16 (regs, ECR_READ_REG_DATA, h - 1);
+	vgaw16 (regs, ECR_MAJ_AXIS_PIX_CNT, w - 1);
+	vgaw16 (regs, ECR_DRAW_CMD, 0x40B1);	
 	DPRINTK("EXIT\n");
 }
 
@@ -2397,83 +2314,84 @@
  */
 static void cv64_dump (void)
 {
+	volatile unsigned char *regs = CyberRegs;
 	DPRINTK("ENTER\n");
         /* Dump the VGA setup values */
-	*(CyberRegs + S3_CRTC_ADR) = 0x00;
-	DPRINTK("CR00 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x01;
-	DPRINTK("CR01 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x02;
-	DPRINTK("CR02 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x03;
-	DPRINTK("CR03 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x04;
-	DPRINTK("CR04 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x05;
-	DPRINTK("CR05 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x06;
-	DPRINTK("CR06 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x07;
-	DPRINTK("CR07 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x08;
-	DPRINTK("CR08 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x09;
-	DPRINTK("CR09 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x10;
-	DPRINTK("CR10 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x11;
-	DPRINTK("CR11 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x12;
-	DPRINTK("CR12 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x13;
-	DPRINTK("CR13 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x15;
-	DPRINTK("CR15 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x16;
-	DPRINTK("CR16 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x36;
-	DPRINTK("CR36 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x37;
-	DPRINTK("CR37 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x42;
-	DPRINTK("CR42 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x43;
-	DPRINTK("CR43 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x50;
-	DPRINTK("CR50 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x51;
-	DPRINTK("CR51 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x53;
-	DPRINTK("CR53 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x58;
-	DPRINTK("CR58 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x59;
-	DPRINTK("CR59 = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x5A;
-	DPRINTK("CR5A = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x5D;
-	DPRINTK("CR5D = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	*(CyberRegs + S3_CRTC_ADR) = 0x5E;
-	DPRINTK("CR5E = %x\n", *(CyberRegs + S3_CRTC_DATA));
-	DPRINTK("MISC = %x\n", *(CyberRegs + GREG_MISC_OUTPUT_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x01;
-	DPRINTK("SR01 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x02;
-	DPRINTK("SR02 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x03;
-	DPRINTK("SR03 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x09;
-	DPRINTK("SR09 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x10;
-	DPRINTK("SR10 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x11;
-	DPRINTK("SR11 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x12;
-	DPRINTK("SR12 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x13;
-	DPRINTK("SR13 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
-	*(CyberRegs + SEQ_ADDRESS) = 0x15;
-	DPRINTK("SR15 = %x\n", *(CyberRegs + SEQ_ADDRESS_R));
+	*(regs + S3_CRTC_ADR) = 0x00;
+	DPRINTK("CR00 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x01;
+	DPRINTK("CR01 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x02;
+	DPRINTK("CR02 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x03;
+	DPRINTK("CR03 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x04;
+	DPRINTK("CR04 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x05;
+	DPRINTK("CR05 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x06;
+	DPRINTK("CR06 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x07;
+	DPRINTK("CR07 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x08;
+	DPRINTK("CR08 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x09;
+	DPRINTK("CR09 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x10;
+	DPRINTK("CR10 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x11;
+	DPRINTK("CR11 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x12;
+	DPRINTK("CR12 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x13;
+	DPRINTK("CR13 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x15;
+	DPRINTK("CR15 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x16;
+	DPRINTK("CR16 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x36;
+	DPRINTK("CR36 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x37;
+	DPRINTK("CR37 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x42;
+	DPRINTK("CR42 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x43;
+	DPRINTK("CR43 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x50;
+	DPRINTK("CR50 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x51;
+	DPRINTK("CR51 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x53;
+	DPRINTK("CR53 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x58;
+	DPRINTK("CR58 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x59;
+	DPRINTK("CR59 = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x5A;
+	DPRINTK("CR5A = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x5D;
+	DPRINTK("CR5D = %x\n", *(regs + S3_CRTC_DATA));
+	*(regs + S3_CRTC_ADR) = 0x5E;
+	DPRINTK("CR5E = %x\n", *(regs + S3_CRTC_DATA));
+	DPRINTK("MISC = %x\n", *(regs + GREG_MISC_OUTPUT_R));
+	*(regs + SEQ_ADDRESS) = 0x01;
+	DPRINTK("SR01 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x02;
+	DPRINTK("SR02 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x03;
+	DPRINTK("SR03 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x09;
+	DPRINTK("SR09 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x10;
+	DPRINTK("SR10 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x11;
+	DPRINTK("SR11 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x12;
+	DPRINTK("SR12 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x13;
+	DPRINTK("SR13 = %x\n", *(regs + SEQ_ADDRESS_R));
+	*(regs + SEQ_ADDRESS) = 0x15;
+	DPRINTK("SR15 = %x\n", *(regs + SEQ_ADDRESS_R));
 	
 	return;
 }
diff -urN linux-2.2.18-pre15/drivers/video/cyberfb.h linux/drivers/video/cyberfb.h
--- linux-2.2.18-pre15/drivers/video/cyberfb.h	Mon Dec 21 14:48:04 1998
+++ linux/drivers/video/cyberfb.h	Mon Dec  3 20:34:12 2001
@@ -128,26 +128,9 @@
 #define GRFBBOPset		0xf	/* 1 */
 
 
-/* Read VGA register */
-#define vgar(ba, reg) (*(((volatile caddr_t)ba)+reg))
-
-/* Write VGA register */
-#define vgaw(ba, reg, val) \
-*(((volatile caddr_t)ba)+reg) = ((val) & 0xff)
-
-/* Read 16 Bit VGA register */
-#define vgar16(ba, reg) (  *((unsigned short *) (((volatile caddr_t)ba)+reg)) )
-
 /* Write 16 Bit VGA register */
 #define vgaw16(ba, reg, val) \
-*((unsigned short *)  (((volatile caddr_t)ba)+reg)) = val
-
-/* Read 32 Bit VGA register */
-#define vgar32(ba, reg) (  *((unsigned long *) (((volatile caddr_t)ba)+reg)) )
-
-/* Write 32 Bit VGA register */
-#define vgaw32(ba, reg, val) \
-     *((unsigned long *)  (((volatile caddr_t)ba)+reg)) = val
+*((unsigned short *)  (((volatile unsigned char *)ba)+reg)) = val
 
 /*
  * Defines for the used register addresses (mw)
@@ -394,20 +377,20 @@
 
 
 #define WGfx(ba, idx, val) \
-do { vgaw(ba, GCT_ADDRESS, idx); vgaw(ba, GCT_ADDRESS_W , val); } while (0)
+do { wb_64(ba, GCT_ADDRESS, idx); wb_64(ba, GCT_ADDRESS_W , val); } while (0)
 
 #define WSeq(ba, idx, val) \
-do { vgaw(ba, SEQ_ADDRESS, idx); vgaw(ba, SEQ_ADDRESS_W , val); } while (0)
+do { wb_64(ba, SEQ_ADDRESS, idx); wb_64(ba, SEQ_ADDRESS_W , val); } while (0)
 
 #define WCrt(ba, idx, val) \
-do { vgaw(ba, CRT_ADDRESS, idx); vgaw(ba, CRT_ADDRESS_W , val); } while (0)
+do { wb_64(ba, CRT_ADDRESS, idx); wb_64(ba, CRT_ADDRESS_W , val); } while (0)
 
 #define WAttr(ba, idx, val) \
 do { \
   unsigned char tmp;\
-  tmp = vgar(ba, ACT_ADDRESS_RESET);\
-  vgaw(ba, ACT_ADDRESS_W, idx);\
-  vgaw(ba, ACT_ADDRESS_W, val);\
+  tmp = rb_64(ba, ACT_ADDRESS_RESET);\
+  wb_64(ba, ACT_ADDRESS_W, idx);\
+  wb_64(ba, ACT_ADDRESS_W, val);\
 } while (0)
 
 #define SetTextPlane(ba, m) \
@@ -420,21 +403,17 @@
      /* prototypes                        */
      /* --------------------------------- */
 
-/* in cvision_core.c */
-inline void __cv_delay(unsigned long usecs);
-inline void GfxBusyWait(volatile caddr_t board);
-inline void GfxFifoWait(volatile caddr_t board);
-inline unsigned char RAttr(volatile caddr_t board, short idx);
-inline unsigned char RSeq(volatile caddr_t board, short idx);
-inline unsigned char RCrt(volatile caddr_t board, short idx);
-inline unsigned char RGfx(volatile caddr_t board, short idx);
+inline unsigned char RAttr(volatile unsigned char * board, short idx);
+inline unsigned char RSeq(volatile unsigned char * board, short idx);
+inline unsigned char RCrt(volatile unsigned char * board, short idx);
+inline unsigned char RGfx(volatile unsigned char * board, short idx);
 inline void cv64_write_port(unsigned short bits,
 			    volatile unsigned char *board);
 inline void cvscreen(int toggle, volatile unsigned char *board);
 inline void gfx_on_off(int toggle, volatile unsigned char *board);
 #if 0
 unsigned short cv64_compute_clock(unsigned long freq);
-int cv_has_4mb(volatile caddr_t fb);
+int cv_has_4mb(volatile unsigned char * fb);
 void cv64_board_init(void);
 void cv64_load_video_mode(struct fb_var_screeninfo *video_mode);
 #endif
diff -urN linux-2.2.18-pre15/drivers/video/fbcon-mac.c linux/drivers/video/fbcon-mac.c
--- linux-2.2.18-pre15/drivers/video/fbcon-mac.c	Mon Dec  3 20:33:36 2001
+++ linux/drivers/video/fbcon-mac.c	Mon Dec  3 20:34:12 2001
@@ -331,7 +331,8 @@
   u16 *dest16, pix16;
   u32 *dest32, pix32;
 
-  if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) {
+  /* There *are* 68k Macs that support more than 832x624, you know :-) */
+  if (pixel_x < 0 || pixel_y < 0 || pixel_x >= p->var.xres || pixel_y >= p->var.yres) {
     int cnt;
     printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y);
     for(cnt = 0; cnt < 100000; cnt++)
diff -urN linux-2.2.18-pre15/drivers/video/macfb.c linux/drivers/video/macfb.c
--- linux-2.2.18-pre15/drivers/video/macfb.c	Mon Dec 21 14:48:04 1998
+++ linux/drivers/video/macfb.c	Mon Dec  3 20:34:12 2001
@@ -1,6 +1,22 @@
-/*
- *	We've been given MAC frame buffer info by the booter. Now go set it up
- */
+/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
+   don't know how to set */
+
+/* (c) 1999 David Huggins-Daines <dhd@debian.org>
+
+   Primarily based on vesafb.c, by Gerd Knorr
+   (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+
+   Also uses information and code from:
+   
+   The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
+   Mellinger, Mikael Forselius, Michael Schmitz, and others.
+
+   valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
+   Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
+   
+   This code is free software.  You may copy, modify, and distribute
+   it subject to the terms and conditions of the GNU General Public
+   License, version 2, or any later version, at your convenience. */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -13,6 +29,7 @@
 #include <linux/delay.h>
 #include <linux/nubus.h>
 #include <linux/init.h>
+#include <linux/fb.h>
 
 #include <asm/setup.h>
 #include <asm/bootinfo.h>
@@ -20,16 +37,120 @@
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/macintosh.h>
-#include <linux/fb.h>
+#include <asm/io.h>
+#include <asm/machw.h>
 
 #include <video/fbcon.h>
-/* conditionalize these ?? */
 #include <video/fbcon-mfb.h>
 #include <video/fbcon-cfb2.h>
 #include <video/fbcon-cfb4.h>
 #include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+
+/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */
+#define DAC_BASE 0x50f24000
+
+/* Some addresses for the DAFB */
+#define DAFB_BASE 0xf9800200
+
+/* Address for the built-in Civic framebuffer in Quadra AVs */
+#define CIVIC_BASE 0x50f30800	/* Only tested on 660AV! */
+
+static int (*macfb_setpalette) (unsigned int regno, unsigned int red,
+				unsigned int green, unsigned int blue) = NULL;
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+				unsigned int green, unsigned int blue);
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+			    unsigned int green, unsigned int blue);
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+			   unsigned int green, unsigned int blue);
+static int mdc_setpalette (unsigned int regno, unsigned int red,
+			   unsigned int green, unsigned int blue);
+static int toby_setpalette (unsigned int regno, unsigned int red,
+			    unsigned int green, unsigned int blue);
+static int civic_setpalette (unsigned int regno, unsigned int red,
+			     unsigned int green, unsigned int blue);
+
+static volatile struct {
+	unsigned char addr;
+	/* Note: word-aligned */
+	char pad[3];
+	unsigned char lut;
+} *valkyrie_cmap_regs;
+
+static volatile struct {
+	unsigned char addr;
+	unsigned char lut;
+} *v8_brazil_cmap_regs;
+
+static volatile struct {
+	unsigned char addr;
+	char pad1[3]; /* word aligned */
+	unsigned char lut;
+	char pad2[3]; /* word aligned */
+	unsigned char cntl; /* a guess as to purpose */
+} *rbv_cmap_regs;
+
+static volatile struct {
+	unsigned long reset;
+	unsigned long pad1[3];
+	unsigned char pad2[3];
+	unsigned char lut;
+} *dafb_cmap_regs;
+
+static volatile struct {
+	unsigned char addr;	/* OFFSET: 0x00 */
+	unsigned char pad1[15];
+	unsigned char lut;	/* OFFSET: 0x10 */
+	unsigned char pad2[15];
+	unsigned char status;	/* OFFSET: 0x20 */
+	unsigned char pad3[7];
+	unsigned long vbl_addr;	/* OFFSET: 0x28 */
+	unsigned int  status2;	/* OFFSET: 0x2C */
+} *civic_cmap_regs;
+
+/* We will leave these the way they are for the time being */
+struct mdc_cmap_regs {
+	char pad1[0x200200];
+	unsigned char addr;
+	char pad2[6];
+	unsigned char lut;
+};
 
-#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+struct toby_cmap_regs {
+	char pad1[0x90018];
+	unsigned char lut; /* TFBClutWDataReg, offset 0x90018 */
+	char pad2[3];
+	unsigned char addr; /* TFBClutAddrReg, offset 0x9001C */
+};
+
+struct jet_cmap_regs {
+	char pad1[0xe0e000];
+	unsigned char addr;
+	unsigned char lut;
+};
+
+#endif
+
+#define PIXEL_TO_MM(a)	(((a)*10)/28)	/* width in mm at 72 dpi */	
+
+static char* video_base;
+static int   video_size;
+static char* video_vbase;        /* mapped */
+
+/* mode */
+static int  video_bpp;
+static int  video_width;
+static int  video_height;
+static int  video_type = FB_TYPE_PACKED_PIXELS;
+static int  video_visual;
+static int  video_linelength;
+static int  video_cmap_len;
+static int  video_slot = 0;
 
 static struct fb_var_screeninfo macfb_defined={
 	0,0,0,0,	/* W,H, W, H (virtual) load xres,xres_virtual*/
@@ -42,38 +163,36 @@
 	{0,0,0},	/* transparency */
 	0,		/* standard pixel format */
 	FB_ACTIVATE_NOW,
-	274,195,	/* 14" monitor *Mikael Nykvist's anyway* */
-	0,		/* The only way to accelerate a mac is .. */
+	-1, -1,
+	FB_ACCEL_NONE,	/* The only way to accelerate a mac is .. */
 	0L,0L,0L,0L,0L,
 	0L,0L,0,	/* No sync info */
 	FB_VMODE_NONINTERLACED,
 	{0,0,0,0,0,0}
 };
 
-#define NUM_TOTAL_MODES		1
-#define NUM_PREDEF_MODES	1
-
 static struct display disp;
 static struct fb_info fb_info;
+static struct { u_short blue, green, red, pad; } palette[256];
+static union {
+#ifdef FBCON_HAS_CFB16
+    u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB24
+    u32 cfb24[16];
+#endif
+#ifdef FBCON_HAS_CFB32
+    u32 cfb32[16];
+#endif
+} fbcon_cmap;
 
-static int inverse = 0;
-
-struct macfb_par
-{
-	void *unused;
-};
-
-static int currcon = 0;
-static int current_par_valid = 0;
-struct macfb_par current_par;
-
-static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres;
-static unsigned long mac_videobase;
-static unsigned long mac_videosize;
+static int             inverse   = 0;
+static int             vidtest   = 0;
+static int             currcon   = 0;
 
-	/*
-	 * Open/Release the frame buffer device
-	 */
+/*
+ * Open/Release the frame buffer device
+ */
 
 static int macfb_open(struct fb_info *info, int user)
 {
@@ -90,105 +209,32 @@
 	return(0);
 }
 
-static void macfb_encode_var(struct fb_var_screeninfo *var, 
-				struct macfb_par *par)
-{
-	memset(var, 0, sizeof(struct fb_var_screeninfo));
-	var->xres=mac_xres;
-	var->yres=mac_yres;
-	var->xres_virtual=mac_vxres;
-	var->yres_virtual=var->yres;
-	var->xoffset=0;
-	var->yoffset=0;
-	var->bits_per_pixel = mac_depth;
-	var->grayscale=0;
-	var->transp.offset=0;
-	var->transp.length=0;
-	var->transp.msb_right=0;
-	var->nonstd=0;
-	var->activate=0;
-	var->height= -1;
-	var->width= -1;
-	var->vmode=FB_VMODE_NONINTERLACED;
-	var->pixclock=0;
-	var->sync=0;
-	var->left_margin=0;
-	var->right_margin=0;
-	var->upper_margin=0;
-	var->lower_margin=0;
-	var->hsync_len=0;
-	var->vsync_len=0;
-	return;
-}
-
-
-static void macfb_get_par(struct macfb_par *par)
-{
-	*par=current_par;
-}
-
-static void macfb_set_par(struct macfb_par *par)
-{
-	current_par_valid=1;
-}
-
-static int fb_update_var(int con, struct fb_info *info)
-{
-	return 0;
-}
-
-static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+static int macfb_update_var(int con, struct fb_info *info)
 {
-	struct macfb_par par;
-	
-	macfb_get_par(&par);
-	macfb_encode_var(var, &par);
 	return 0;
 }
 
-extern int console_loglevel;
-
-static void macfb_encode_fix(struct fb_fix_screeninfo *fix, 
-				struct macfb_par *par)
-{
-	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id,"Macintosh");
-
-	/*
-	 * fbmem.c accepts non page aligned mappings now!
-	 */
-	fix->smem_start=(char *)mac_videobase;
-	fix->smem_len=mac_videosize;
-	fix->type = FB_TYPE_PACKED_PIXELS;
-	if (mac_depth == 1)
-		fix->visual = FB_VISUAL_MONO01;
-	else
-		fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
-	fix->xpanstep=0;
-	fix->ypanstep=0;
-	fix->ywrapstep=0;
-	fix->line_length=mac_xbytes;
-	return;
-}
-
 static int macfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info)
 {
-	struct macfb_par par;
-	macfb_get_par(&par);
-	macfb_encode_fix(fix, &par);
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id, "Mac Generic");
+
+	fix->smem_start=(char *) video_base;
+	fix->smem_len = video_size;
+	fix->type = video_type;
+	fix->visual = video_visual;
+	fix->xpanstep = 0;
+	fix->ypanstep = 0;
+	fix->line_length=video_linelength;
 	return 0;
 }
 
 static int macfb_get_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
-	struct macfb_par par;
 	if(con==-1)
-	{
-		macfb_get_par(&par);
-		macfb_encode_var(var, &par);
-	}
+		memcpy(var, &macfb_defined, sizeof(struct fb_var_screeninfo));
 	else
 		*var=fb_display[con].var;
 	return 0;
@@ -204,9 +250,10 @@
 	else
 		display = &disp;	/* used during initialization */
 
-	macfb_get_fix(&fix, con, 0);
+	macfb_get_fix(&fix, con, &fb_info);
 
-	display->screen_base = fix.smem_start;
+	memset(display, 0, sizeof(struct display));
+	display->screen_base = video_vbase;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -215,235 +262,568 @@
 	display->line_length = fix.line_length;
 	display->next_line = fix.line_length;
 	display->can_soft_blank = 0;
-	display->inverse =
-	    (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+	display->inverse = inverse;
+	display->scrollmode = SCROLL_YREDRAW;
+	macfb_get_var(&display->var, -1, &fb_info);
 
-	switch (mac_depth) {
+	switch (video_bpp) {
 #ifdef FBCON_HAS_MFB
-	    case 1:
+	case 1:
 		display->dispsw = &fbcon_mfb;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB2
-	    case 2:
+	case 2:
 		display->dispsw = &fbcon_cfb2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB4
-	    case 4:
+	case 4:
 		display->dispsw = &fbcon_cfb4;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB8
-	    case 8:
+	case 8:
 		display->dispsw = &fbcon_cfb8;
 		break;
 #endif
-	    default:
-		display->dispsw = &fbcon_dummy;
+#ifdef FBCON_HAS_CFB16
+	case 15:
+	case 16:
+		display->dispsw = &fbcon_cfb16;
+		display->dispsw_data = fbcon_cmap.cfb16;
 		break;
+#endif
+#ifdef FBCON_HAS_CFB24
+	case 24:
+		display->dispsw = &fbcon_cfb24;
+		display->dispsw_data = fbcon_cmap.cfb24;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		display->dispsw = &fbcon_cfb32;
+		display->dispsw_data = fbcon_cmap.cfb32;
+		break;
+#endif
+	default:
+		display->dispsw = &fbcon_dummy;
+		return;
 	}
 }
 
 static int macfb_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
-	int err;
+	static int first = 1;
+
+	if (var->xres           != macfb_defined.xres           ||
+	    var->yres           != macfb_defined.yres           ||
+	    var->xres_virtual   != macfb_defined.xres_virtual   ||
+	    var->yres_virtual   != macfb_defined.yres           ||
+	    var->xoffset                                        ||
+	    var->bits_per_pixel != macfb_defined.bits_per_pixel ||
+	    var->nonstd) {
+		if (first) {
+			printk("macfb does not support changing the video mode\n");
+			first = 0;
+		}
+		return -EINVAL;
+	}
+
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+		return 0;
+
+	if (var->yoffset)
+		return -EINVAL;
+	return 0;
+}
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+				unsigned int green, unsigned int blue)
+{
+	unsigned long flags;
+	
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+
+	save_flags(flags);
+	cli();
+	
+	/* tell clut which address to fill */
+	writeb(regno, &valkyrie_cmap_regs->addr);
+	nop();
+
+	/* send one color channel at a time */
+	writeb(red, &valkyrie_cmap_regs->lut);
+	nop();
+	writeb(green, &valkyrie_cmap_regs->lut);
+	nop();
+	writeb(blue, &valkyrie_cmap_regs->lut);
+
+	restore_flags(flags);
+
+	return 0;
+}
+
+/* Unlike the Valkyrie, the DAFB cannot set individual colormap
+   registers.  Therefore, we do what the MacOS driver does (no
+   kidding!) and simply set them one by one until we hit the one we
+   want. */
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+			    unsigned int green, unsigned int blue)
+{
+	/* FIXME: really, really need to use ioremap() here,
+           phys_to_virt() doesn't work anymore */
+	static int lastreg = -1;
+	unsigned long flags;
+	
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+
+	save_flags(flags);
+	cli();
+	
+	/* fbcon will set an entire colourmap, but X won't.  Hopefully
+	   this should accomodate both of them */
+	if (regno != lastreg+1) {
+		int i;
+		
+		/* Stab in the dark trying to reset the CLUT pointer */
+		writel(0, &dafb_cmap_regs->reset);
+		nop();
+		
+		/* Loop until we get to the register we want */
+		for (i = 0; i < regno; i++) {
+			writeb(palette[i].red >> 8, &dafb_cmap_regs->lut);
+			nop();
+			writeb(palette[i].green >> 8, &dafb_cmap_regs->lut);
+			nop();
+			writeb(palette[i].blue >> 8, &dafb_cmap_regs->lut);
+			nop();
+		}
+	}
+		
+	writeb(red, &dafb_cmap_regs->lut);
+	nop();
+	writeb(green, &dafb_cmap_regs->lut);
+	nop();
+	writeb(blue, &dafb_cmap_regs->lut);
+	
+	restore_flags(flags);
+	
+	lastreg = regno;
+	return 0;
+}
+
+/* V8 and Brazil seem to use the same DAC.  Sonora does as well. */
+static int v8_brazil_setpalette (unsigned int regno, unsigned int red,
+				 unsigned int green, unsigned int blue)
+{
+	unsigned char _red  =red>>8;
+	unsigned char _green=green>>8;
+	unsigned char _blue =blue>>8;
+	unsigned char _regno;
+	unsigned long flags;
+
+	if (video_bpp>8) return 1; /* failsafe */
+
+	save_flags(flags);
+	cli();
+
+	/* On these chips, the CLUT register numbers are spread out
+	   across the register space.  Thus:
+
+	   In 8bpp, all regnos are valid.
+	   
+	   In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc
+	   
+	   In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff */
+  	_regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+	writeb(_regno, &v8_brazil_cmap_regs->addr); nop();
+
+	/* send one color channel at a time */
+	writeb(_red, &v8_brazil_cmap_regs->lut); nop();
+	writeb(_green, &v8_brazil_cmap_regs->lut); nop();
+	writeb(_blue, &v8_brazil_cmap_regs->lut);
+
+	restore_flags(flags);
+	
+	return 0;
+}
+
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+			   unsigned int green, unsigned int blue)
+{
+	/* use MSBs */
+	unsigned char _red  =red>>8;
+	unsigned char _green=green>>8;
+	unsigned char _blue =blue>>8;
+	unsigned char _regno;
+	unsigned long flags;
+
+	if (video_bpp>8) return 1; /* failsafe */
+
+	save_flags(flags);
+	cli();
+	
+	/* From the VideoToolbox driver.  Seems to be saying that
+	 * regno #254 and #255 are the important ones for 1-bit color,
+	 * regno #252-255 are the important ones for 2-bit color, etc.
+	 */
+	_regno = regno + (256-(1<<video_bpp));
+
+	/* reset clut? (VideoToolbox sez "not necessary") */
+	writeb(0xFF, &rbv_cmap_regs->cntl); nop();
+	
+	/* tell clut which address to use. */
+	writeb(_regno, &rbv_cmap_regs->addr); nop();
+	
+	/* send one color channel at a time. */
+	writeb(_red,   &rbv_cmap_regs->lut); nop();
+	writeb(_green, &rbv_cmap_regs->lut); nop();
+	writeb(_blue,  &rbv_cmap_regs->lut);
+	
+	restore_flags(flags);
+	/* done. */
+	return 0;
+}
+
+/* Macintosh Display Card (8x24) */
+static int mdc_setpalette(unsigned int regno, unsigned int red,
+			  unsigned int green, unsigned int blue)
+{
+	volatile struct mdc_cmap_regs *cmap_regs =
+		nubus_slot_addr(video_slot);
+	/* use MSBs */
+	unsigned char _red  =red>>8;
+	unsigned char _green=green>>8;
+	unsigned char _blue =blue>>8;
+	unsigned char _regno=regno;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	
+	/* the nop's are there to order writes. */
+	writeb(_regno, &cmap_regs->addr); nop();
+	writeb(_red, &cmap_regs->lut);    nop();
+	writeb(_green, &cmap_regs->lut);  nop();
+	writeb(_blue, &cmap_regs->lut);
+
+	restore_flags(flags);
+	return 0;
+}
+
+/* Toby frame buffer */
+static int toby_setpalette(unsigned int regno, unsigned int red,
+			   unsigned int green, unsigned int blue)
+{
+	volatile struct toby_cmap_regs *cmap_regs =
+		nubus_slot_addr(video_slot);
+	/* use MSBs */
+	unsigned char _red  =~(red>>8);
+	unsigned char _green=~(green>>8);
+	unsigned char _blue =~(blue>>8);
+	unsigned char _regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	
+	writeb(_regno, &cmap_regs->addr); nop();
+	writeb(_red, &cmap_regs->lut);    nop();
+	writeb(_green, &cmap_regs->lut);  nop();
+	writeb(_blue, &cmap_regs->lut);
+
+	restore_flags(flags);
+	return 0;
+}
+
+/* Jet frame buffer */
+static int jet_setpalette(unsigned int regno, unsigned int red,
+			  unsigned int green, unsigned int blue)
+{
+	volatile struct jet_cmap_regs *cmap_regs =
+		nubus_slot_addr(video_slot);
+	/* use MSBs */
+	unsigned char _red   = (red>>8);
+	unsigned char _green = (green>>8);
+	unsigned char _blue  = (blue>>8);
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
 	
-	if ((err=do_fb_set_var(var, 1)))
-		return err;
+	writeb(regno, &cmap_regs->addr); nop();
+	writeb(_red, &cmap_regs->lut); nop();
+	writeb(_green, &cmap_regs->lut); nop();
+	writeb(_blue, &cmap_regs->lut);
+
+	restore_flags(flags);
 	return 0;
 }
 
 /*
- * Color map handling - hardcoded maps!! 
+ * Civic framebuffer -- Quadra AV built-in video.  A chip
+ * called Sebastian holds the actual color palettes, and
+ * apparently, there are two different banks of 512K RAM 
+ * which can act as separate framebuffers for doing video
+ * input and viewing the screen at the same time!  The 840AV
+ * Can add another 1MB RAM to give the two framebuffers 
+ * 1MB RAM apiece.
  *
- * 2.0 color map primitives, copied from atafb.c
- */
- 
-/*
- * should be kmalloc'ed on request
+ * FIXME: this doesn't seem to work anymore.
  */
-static short red256[256], green256[256], blue256[256];
+static int civic_setpalette (unsigned int regno, unsigned int red,
+			     unsigned int green, unsigned int blue)
+{
+	static int lastreg = -1;
+	unsigned long flags;
+	int clut_status;
+	
+	if (video_bpp > 8) return 1; /* failsafe */
 
-static short red16[]=
-	{ 0x0000,0x0000,0x0000,0x0000,0x8080,0x8080,0x8080,0xc0c0,
-	  0x8080,0x0000,0x0000,0x0000,0xffff,0xffff,0xffff,0xffff};
-static short green16[]=
-	{ 0x0000,0x0000,0x8080,0x8080,0x0000,0x0000,0x8080,0xc0c0,
-	  0x8080,0x0000,0xffff,0xffff,0x0000,0x0000,0xffff,0xffff};
-static short blue16[]=
-	{ 0x0000,0x8080,0x0000,0x8080,0x0000,0x8080,0x0000,0xc0c0,
-	  0x8080,0xffff,0x0000,0xffff,0x0000,0xffff,0x0000,0xffff};
-
-static short red4[]=
-	{ 0x0000,0x8080,0xffff,0xffff};
-static short green4[]=
-	{ 0x0000,0x8080,0x0000,0xffff};
-static short blue4[]=
-	{ 0x0000,0x8080,0x0000,0xffff};
-
-static short red2[]=
-	{ 0x0000,0xffff};
-static short green2[]=
-	{ 0x0000,0xffff};
-static short blue2[]=
-	{ 0x0000,0xffff};
-
-struct fb_cmap default_256_colors = { 0, 256, red256, green256, blue256, NULL };
-struct fb_cmap default_16_colors  = { 0, 16, red16, green16, blue16, NULL };
-struct fb_cmap default_4_colors   = { 0, 4, red4, green4, blue4, NULL };
-struct fb_cmap default_2_colors   = { 0, 2, red2, green2, blue2, NULL };
-
-static int mac_set_cmap256(struct fb_cmap* cmap)
-{
-	int i,start;
-	unsigned short *red,*green,*blue;
-	unsigned short cval[] = {0xffff, 0xcccc, 0x9999, 
-				 0x6666, 0x3333, 0x0000 };
-	unsigned short gval[] = {0x0a0a, 0x1414, 0x1e1e,
-				 0x2828, 0x3232, 0x3c3c,
-				 0x4646, 0x5050, 0x5a5a,
-				 0x6464, 0x6e6e, 0x7878,
-				 0x8282, 0x8c8c, 0x9696,
-				 0xa0a0, 0xaaaa, 0xb4b4,
-				 0xbebe, 0xc8c8, 0xd2d2,
-				 0xdcdc, 0xe6e6, 0xf0f0};
-
-	red=cmap->red;
-	green=cmap->green;
-	blue=cmap->blue;
-	start=cmap->start;
+	red   >>= 8;
+	green >>= 8;
+	blue  >>= 8;
 
-	if (start < 0)
-		return -EINVAL;
-	if (cmap->len < 255)
-		return -EINVAL;
-	/* 16 ANSI colors */
-	for (i=0 ; i < 16 ; i++) {
-		*red++   = red16[i];
-		*green++ = green16[i];
-		*blue++  = blue16[i];
+	save_flags(flags);
+	cli();
+	
+	/*
+	 * Set the register address
+	 */
+	writeb(regno, &civic_cmap_regs->addr); nop();
+
+	/*
+	 * Wait for VBL interrupt here;
+	 * They're usually not enabled from Penguin, so we won't check
+	 */
+#if 0
+	{
+#define CIVIC_VBL_OFFSET	0x120
+		volatile unsigned long *vbl = readl(civic_cmap_regs->vbl_addr + CIVIC_VBL_OFFSET);
+		/* do interrupt setup stuff here? */
+		*vbl = 0L; nop();	/* clear */
+		*vbl = 1L; nop();	/* set */
+		while (*vbl != 0L)	/* wait for next vbl */
+		{
+			usleep(10);	/* needed? */
+		}
+		/* do interrupt shutdown stuff here? */
 	}
-	/* 216 colors (6x6x6) map) */
-	for (i=16 ; i < 232 ; i++) {
-		*red++   = cval[(i-16)/36];
-		*green++ = cval[((i-16)/6)%6];
-		*blue++  = cval[(i-16)%6];
+#endif
+
+	/*
+	 * Grab a status word and do some checking;
+	 * Then finally write the clut!
+	 */
+	clut_status =  readb(&civic_cmap_regs->status2);
+
+	if ((clut_status & 0x0008) == 0)
+	{
+#if 0
+		if ((clut_status & 0x000D) != 0)
+		{
+			writeb(0x00, &civic_cmap_regs->lut); nop();
+			writeb(0x00, &civic_cmap_regs->lut); nop();
+		}
+#endif
+
+		writeb(  red, &civic_cmap_regs->lut); nop();
+		writeb(green, &civic_cmap_regs->lut); nop();
+		writeb( blue, &civic_cmap_regs->lut); nop();
+		writeb( 0x00, &civic_cmap_regs->lut); nop();
 	}
-	/* 24 grays */
-	for (i=232 ; i < 256 ; i++) {
-		*red = *green = *blue = gval[i-232];
-		red++;
-		green++;
-		blue++;
+	else
+	{
+		unsigned char junk;
+
+		junk = readb(&civic_cmap_regs->lut); nop();
+		junk = readb(&civic_cmap_regs->lut); nop();
+		junk = readb(&civic_cmap_regs->lut); nop();
+		junk = readb(&civic_cmap_regs->lut); nop();
+
+		if ((clut_status & 0x000D) != 0)
+		{
+			writeb(0x00, &civic_cmap_regs->lut); nop();
+			writeb(0x00, &civic_cmap_regs->lut); nop();
+		}
+
+		writeb(  red, &civic_cmap_regs->lut); nop();
+		writeb(green, &civic_cmap_regs->lut); nop();
+		writeb( blue, &civic_cmap_regs->lut); nop();
+		writeb( junk, &civic_cmap_regs->lut); nop();
 	}
+
+	restore_flags(flags);
+
+	lastreg = regno;
 	return 0;
 }
+#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB4 || FBCON_HAS_CFB2 */
 
-static struct fb_cmap * mac_get_default_cmap(int bpp)
+static int macfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			   unsigned *blue, unsigned *transp,
+			   struct fb_info *fb_info)
 {
-	if (bpp == 1)
-		return &default_2_colors;
-	if (bpp == 2)
-		return &default_4_colors;
-	if (bpp == 4)
-		return &default_16_colors;
-	return &default_256_colors;
-}
-
-static void memcpy_fs(int fsfromto, void *to, void *from, int len)
-{
-    switch (fsfromto) {
-    	case 0:
-    	    memcpy(to, from, len);
-    	    return;
-    	case 1:
-    	    copy_from_user(to, from, len);
-    	    return;
-    	case 2:
-    	    copy_to_user(to, from, len);
-    	    return;
-    }
+	/*
+	 *  Read a single color register and split it into colors/transparent.
+	 *  Return != 0 for invalid regno.
+	 */
+
+	if (regno >= video_cmap_len)
+		return 1;
+
+	*red   = palette[regno].red;
+	*green = palette[regno].green;
+	*blue  = palette[regno].blue;
+	*transp = 0;
+	return 0;
 }
 
-static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *fb_info)
 {
-	int size;
-	int tooff=0, fromoff=0;
+	/*
+	 *  Set a single color register. The values supplied are
+	 *  already rounded down to the hardware's capabilities
+	 *  (according to the entries in the `var' structure). Return
+	 *  != 0 for invalid regno.
+	 */
+	
+	if (regno >= video_cmap_len)
+		return 1;
 
-	if (to->start > from->start)
-		fromoff=to->start-from->start;
-	else
-		tooff=from->start-to->start;			
-	size=to->len-tooff;
-	if (size > from->len-fromoff)
-		size=from->len-fromoff;
-	if (size < 0)
+	palette[regno].red   = red;
+	palette[regno].green = green;
+	palette[regno].blue  = blue;
+
+	switch (video_bpp) {
+#ifdef FBCON_HAS_MFB
+	case 1:
+		/* We shouldn't get here */
+		break;
+#endif
+#ifdef FBCON_HAS_CFB2
+	case 2:
+		if (macfb_setpalette)
+			macfb_setpalette(regno, red, green, blue);
+		else
+			return 1;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB4
+	case 4:
+		if (macfb_setpalette)
+			macfb_setpalette(regno, red, green, blue);
+		else
+			return 1;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		if (macfb_setpalette)
+			macfb_setpalette(regno, red, green, blue);
+		else
+			return 1;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 15:
+	case 16:
+		/* 1:5:5:5 */
+		fbcon_cmap.cfb16[regno] =
+			((red   & 0xf800) >>  1) |
+			((green & 0xf800) >>  6) |
+			((blue  & 0xf800) >> 11) |
+			((transp != 0) << 15);
+		break;
+#endif
+		/* I'm pretty sure that one or the other of these
+		   doesn't exist on 68k Macs */
+#ifdef FBCON_HAS_CFB24
+	case 24:
+		red   >>= 8;
+		green >>= 8;
+		blue  >>= 8;
+		fbcon_cmap.cfb24[regno] =
+			(red   << macfb_defined.red.offset)   |
+			(green << macfb_defined.green.offset) |
+			(blue  << macfb_defined.blue.offset);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		red   >>= 8;
+		green >>= 8;
+		blue  >>= 8;
+		fbcon_cmap.cfb32[regno] =
+			(red   << macfb_defined.red.offset)   |
+			(green << macfb_defined.green.offset) |
+			(blue  << macfb_defined.blue.offset);
+		break;
+#endif
+    }
+    return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+	if (con != currcon)
 		return;
-	size*=sizeof(unsigned short);
-	memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
-	memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
-	memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
-	if (from->transp && to->transp)
-		memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+	if (fb_display[con].cmap.len)
+		fb_set_cmap(&fb_display[con].cmap, 1, macfb_setcolreg, info);
+	else
+		fb_set_cmap(fb_default_cmap(video_cmap_len), 1,
+			    macfb_setcolreg, info);
 }
- 
+
 static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-#if 0
-	printk("macfb_get_cmap: not supported!\n");
-	/* interferes with X11 */
-	if (console_loglevel < 7)
-		return -EINVAL;
 	if (con == currcon) /* current console? */
-		return fb_get_cmap(cmap, kspc, 0 /*offb_getcolreg*/, info);
+		return fb_get_cmap(cmap, kspc, macfb_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_copy_cmap(fb_default_cmap(video_cmap_len),
 		     cmap, kspc ? 0 : 2);
-#endif
-	copy_cmap(mac_get_default_cmap(fb_display[con].var.bits_per_pixel),
-	     cmap, kspc ? 0 : 2);
 	return 0;
-
 }
 
 static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-#if 0
-	printk("macfb_set_cmap: not supported!\n");
-	if (console_loglevel < 7)
-		return -EINVAL;
+	int err;
+
 	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				 1<<fb_display[con].var.bits_per_pixel, 0)))
-		return err;
+		err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
+		if (err)
+			return err;
 	}
 	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, 1 /*offb_setcolreg*/, info);
+		return fb_set_cmap(cmap, kspc, macfb_setcolreg, info);
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-#endif
 	return 0;
 }
 
-static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
-			     struct fb_info *info)
+static int macfb_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg, int con,
+		       struct fb_info *info)
 {
-	/* no panning */
-	printk("macfb_pan: not supported!\n");
 	return -EINVAL;
 }
 
-static int macfb_ioctl(struct inode *inode, struct file *file, 
-		       unsigned int cmd, unsigned long arg, int con,
-		       struct fb_info *info)
+static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info)
 {
-	printk("macfb_ioctl: not supported!\n");
 	return -EINVAL;
 }
 
@@ -456,135 +836,393 @@
 	macfb_get_cmap,
 	macfb_set_cmap,
 	macfb_pan_display,
-	macfb_ioctl
+	macfb_ioctl,
+	NULL
 };
 
 void macfb_setup(char *options, int *ints)
 {
-    char *this_opt;
-
-    fb_info.fontname[0] = '\0';
-
-    if (!options || !*options)
+	char *this_opt;
+	
+	fb_info.fontname[0] = '\0';
+	
+	if (!options || !*options)
 		return;
-     
-    for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
-	if (!*this_opt) continue;
-
-	if (! strcmp(this_opt, "inverse"))
-		inverse=1;
-	else if (!strncmp(this_opt, "font:", 5)) {
-	   strcpy(fb_info.fontname, this_opt+5);
-	   printk("macfb_setup: option %s\n", this_opt);
+	
+	for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+		if (!*this_opt) continue;
+		
+		if (! strcmp(this_opt, "inverse"))
+			inverse=1;
+		else if (!strncmp(this_opt, "font:", 5))
+			strcpy(fb_info.fontname, this_opt+5);
+		/* This means "turn on experimental CLUT code" */
+		else if (!strcmp(this_opt, "vidtest"))
+			vidtest=1;
 	}
-    }
 }
 
 static int macfb_switch(int con, struct fb_info *info)
 {
-	do_fb_set_var(&fb_display[con].var,1);
-	currcon=con;
-	return 0;
+	/* Do we have to save the colormap? */
+	if (fb_display[currcon].cmap.len)
+		fb_get_cmap(&fb_display[currcon].cmap, 1, macfb_getcolreg,
+			    info);
+	
+	currcon = con;
+	/* Install new colormap */
+	do_install_cmap(con, info);
+	macfb_update_var(con, info);
+	return 1;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
 static void macfb_blank(int blank, struct fb_info *info)
 {
 	/* Not supported */
 }
 
-/*
- *	Nubus call back. This will give us our board identity and also
- *	other useful info we need later
- */
- 
-static int nubus_video_card(struct nubus_device_specifier *ns, int slot, struct nubus_type *nt)
-{
-	if(nt->category==NUBUS_CAT_DISPLAY)
-		return 0;
-	/* Claim all video cards. We don't yet do driver specifics though. */
-	return -ENODEV;
-}
-
-static struct nubus_device_specifier nb_video={
-	nubus_video_card,
-	NULL
-};
-
 __initfunc(void macfb_init(void))
 {
-	/* nubus_remap the video .. */
+	struct nubus_dev* ndev = NULL;
+	int video_is_nubus = 0;
 
 	if (!MACH_IS_MAC) 
 		return;
 
-	mac_xres=mac_bi_data.dimensions&0xFFFF;
-	mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16;
-	mac_depth=mac_bi_data.videodepth;
-	mac_xbytes=mac_bi_data.videorow;
-	mac_vxres = (mac_xbytes/mac_depth)*8;
-	mac_videosize=mac_xbytes*mac_yres;
-	mac_videobase=mac_bi_data.videoaddr;
-
-	printk("macfb_init: xres %d yres %d bpp %d addr %lx size %ld \n",
-		mac_xres, mac_yres, mac_depth, mac_videobase, mac_videosize);
-
+	/* There can only be one internal video controller anyway so
+	   we're not too worried about this */
+	video_width      = mac_bi_data.dimensions & 0xFFFF;
+	video_height     = mac_bi_data.dimensions >> 16;
+	video_bpp        = mac_bi_data.videodepth;
+	video_linelength = mac_bi_data.videorow;
+	video_size       = video_linelength * video_height;
+	/* Note: physical address (since 2.1.127) */
+	video_base       = (void*) mac_bi_data.videoaddr;
+	/* This is actually redundant with the initial mappings.
+	   However, there are some non-obvious aspects to the way
+	   those mappings are set up, so this is in fact the safest
+	   way to ensure that this driver will work on every possible
+	   Mac */
+	video_vbase	 = ioremap(mac_bi_data.videoaddr, video_size);
+	
+	printk("macfb: framebuffer at 0x%p, mapped to 0x%p, size %dk\n",
+	       video_base, video_vbase, video_size/1024);
+	printk("macfb: mode is %dx%dx%d, linelength=%d\n",
+	       video_width, video_height, video_bpp, video_linelength);
+	
 	/*
 	 *	Fill in the available video resolution
 	 */
 	 
-	 macfb_defined.xres=mac_xres;
-	 macfb_defined.yres=mac_yres;
-	 macfb_defined.xres_virtual=mac_vxres;
-	 macfb_defined.yres_virtual=mac_yres;
-	 macfb_defined.bits_per_pixel=mac_depth;
-	 
-	 
-	/*
-	 *	Let there be consoles..
+	macfb_defined.xres           = video_width;
+	macfb_defined.yres           = video_height;
+	macfb_defined.xres_virtual   = video_width;
+	macfb_defined.yres_virtual   = video_height;
+	macfb_defined.bits_per_pixel = video_bpp;
+	macfb_defined.height = PIXEL_TO_MM(macfb_defined.yres);
+	macfb_defined.width  = PIXEL_TO_MM(macfb_defined.xres);	 
+
+	printk("macfb: scrolling: redraw\n");
+	macfb_defined.yres_virtual = video_height;
+
+	/* some dummy values for timing to make fbset happy */
+	macfb_defined.pixclock     = 10000000 / video_width * 1000 / video_height;
+	macfb_defined.left_margin  = (video_width / 8) & 0xf8;
+	macfb_defined.right_margin = 32;
+	macfb_defined.upper_margin = 16;
+	macfb_defined.lower_margin = 4;
+	macfb_defined.hsync_len    = (video_width / 8) & 0xf8;
+	macfb_defined.vsync_len    = 4;
+
+	switch (video_bpp) {
+	case 1:
+		/* XXX: I think this will catch any program that tries
+		   to do FBIO_PUTCMAP when the visual is monochrome */
+		video_cmap_len = 0;
+		video_visual = FB_VISUAL_MONO01;
+		break;
+	case 2:
+	case 4:
+	case 8:
+		macfb_defined.red.length = video_bpp;
+		macfb_defined.green.length = video_bpp;
+		macfb_defined.blue.length = video_bpp;
+		video_cmap_len = 1 << video_bpp;
+		video_visual = FB_VISUAL_PSEUDOCOLOR;
+		break;
+	case 16:
+		macfb_defined.transp.offset = 15;
+		macfb_defined.transp.length = 1;
+		macfb_defined.red.offset = 10;
+		macfb_defined.red.length = 5;
+		macfb_defined.green.offset = 5;
+		macfb_defined.green.length = 5;
+		macfb_defined.blue.offset = 0;
+		macfb_defined.blue.length = 5;
+		printk("macfb: directcolor: "
+		       "size=1:5:5:5, shift=15:10:5:0\n");
+		video_cmap_len = 16;
+		/* Should actually be FB_VISUAL_DIRECTCOLOR, but this
+		   works too */
+		video_visual = FB_VISUAL_TRUECOLOR;
+		break;
+	case 24:
+	case 32:
+		/* XXX: have to test these... can any 68k Macs
+		   actually do this on internal video? */
+		macfb_defined.red.offset = 16;
+		macfb_defined.red.length = 8;
+		macfb_defined.green.offset = 8;
+		macfb_defined.green.length = 8;
+		macfb_defined.blue.offset = 0;
+		macfb_defined.blue.length = 8;
+		printk("macfb: truecolor: "
+		       "size=0:8:8:8, shift=0:16:8:0\n");
+		video_cmap_len = 16;
+		video_visual = FB_VISUAL_TRUECOLOR;
+	default:
+		video_cmap_len = 0;
+		video_visual = FB_VISUAL_MONO01;
+		printk("macfb: unknown or unsupported bit depth: %d\n", video_bpp);
+		break;
+	}
+	
+	/* Hardware dependent stuff */
+	/*  We take a wild guess that if the video physical address is
+	 *  in nubus slot space, that the nubus card is driving video.
+	 *  Penguin really ought to tell us whether we are using internal
+	 *  video or not.
 	 */
-	strcpy(fb_info.modename, "Macintosh Builtin ");
-	fb_info.changevar = NULL;
-	fb_info.node = -1;
-	fb_info.fbops = &macfb_ops;
-	fb_info.disp=&disp;
-	fb_info.switch_con=&macfb_switch;
-	fb_info.updatevar=&fb_update_var;
-	fb_info.blank=&macfb_blank;
-	fb_info.flags = FBINFO_FLAG_DEFAULT;
-	do_fb_set_var(&macfb_defined,1);
-
-	macfb_get_var(&disp.var, -1, &fb_info);
-	macfb_set_disp(-1);
+	/* Hopefully we only find one of them.  Otherwise our NuBus
+           code is really broken :-) */
 
-	/*
-	 *	Fill in the 8 bit color table if required
-	 */
-	if (mac_depth == 8)
-		mac_set_cmap256(&default_256_colors);
-	 	
-	/*
-	 *	Register the nubus hook
-	 */
-	 
-	register_nubus_device(&nb_video);
+	while ((ndev = nubus_find_type(NUBUS_CAT_DISPLAY, NUBUS_TYPE_VIDEO, ndev))
+		!= NULL)
+	{
+		if (!(mac_bi_data.videoaddr >= ndev->board->slot_addr
+		      && (mac_bi_data.videoaddr <
+			  (unsigned long)nubus_slot_addr(ndev->board->slot+1))))
+			continue;
+		video_is_nubus = 1;
+		/* We should probably just use the slot address... */
+		video_slot = ndev->board->slot;
+
+		switch(ndev->dr_hw) {
+		case NUBUS_DRHW_APPLE_MDC:
+			strcpy( fb_info.modename, "Macintosh Display Card" );
+			macfb_setpalette = mdc_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			break;
+		case NUBUS_DRHW_APPLE_TFB:
+			strcpy( fb_info.modename, "Toby" );
+			macfb_setpalette = toby_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			break;
+		case NUBUS_DRHW_APPLE_JET:
+			strcpy(fb_info.modename, "Jet");
+			macfb_setpalette = jet_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			break;			
+		default:
+			strcpy( fb_info.modename, "Generic NuBus" );
+			break;
+		}
+	}
 
+	/* If it's not a NuBus card, it must be internal video */
+	/* FIXME: this function is getting way too big.  (this driver
+           is too...) */
+	if (!video_is_nubus)
+		switch( mac_bi_data.id )
+		{
+			/* These don't have onboard video.  Eventually, we may
+			   be able to write separate framebuffer drivers for
+			   them (tobyfb.c, hiresfb.c, etc, etc) */
+		case MAC_MODEL_II:
+		case MAC_MODEL_IIX:
+		case MAC_MODEL_IICX:
+		case MAC_MODEL_IIFX:
+			strcpy( fb_info.modename, "Generic NuBus" );
+			break;
+
+			/* Valkyrie Quadras */
+		case MAC_MODEL_Q630:
+			/* I'm not sure about this one */
+		case MAC_MODEL_P588:
+			strcpy( fb_info.modename, "Valkyrie built-in" );
+			macfb_setpalette = valkyrie_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			valkyrie_cmap_regs = ioremap(DAC_BASE, 0x1000);
+			break;
+
+			/* DAFB Quadras */
+			/* Note: these first five have the v7 DAFB, which is
+			   known to be rather unlike the ones used in the
+			   other models */
+		case MAC_MODEL_P475:
+		case MAC_MODEL_P475F:
+		case MAC_MODEL_P575:
+		case MAC_MODEL_Q605:
+		case MAC_MODEL_Q605_ACC:
+	
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q610:
+		case MAC_MODEL_C650:
+		case MAC_MODEL_C610:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			strcpy( fb_info.modename, "DAFB built-in" );
+			macfb_setpalette = dafb_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000);
+			break;
+
+			/* LC II uses the V8 framebuffer */
+		case MAC_MODEL_LCII:
+			strcpy( fb_info.modename, "V8 built-in" );
+			macfb_setpalette = v8_brazil_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+			break;
+		
+			/* IIvi, IIvx use the "Brazil" framebuffer (which is
+			   very much like the V8, it seems, and probably uses
+			   the same DAC) */
+		case MAC_MODEL_IIVI:
+		case MAC_MODEL_IIVX:
+		case MAC_MODEL_P600:
+			strcpy( fb_info.modename, "Brazil built-in" );
+			macfb_setpalette = v8_brazil_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+			break;
+		
+			/* LC III (and friends) use the Sonora framebuffer */
+			/* Incidentally this is also used in the non-AV models
+			   of the x100 PowerMacs */
+			/* These do in fact seem to use the same DAC interface
+			   as the LC II. */
+		case MAC_MODEL_LCIII:
+		case MAC_MODEL_P520:
+		case MAC_MODEL_P550:
+		case MAC_MODEL_P460:
+			macfb_setpalette = v8_brazil_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			strcpy( fb_info.modename, "Sonora built-in" );
+			v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+			break;
+
+			/* IIci and IIsi use the infamous RBV chip
+                           (the IIsi is just a rebadged and crippled
+                           IIci in a different case, BTW) */
+		case MAC_MODEL_IICI:
+		case MAC_MODEL_IISI:
+			macfb_setpalette = rbv_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			strcpy( fb_info.modename, "RBV built-in" );
+			rbv_cmap_regs = ioremap(DAC_BASE, 0x1000);
+			break;
+
+			/* AVs use the Civic framebuffer */
+		case MAC_MODEL_Q840:
+		case MAC_MODEL_C660:
+			macfb_setpalette = civic_setpalette;
+			macfb_defined.activate = FB_ACTIVATE_NOW;
+			strcpy( fb_info.modename, "Civic built-in" );
+			civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000);
+			break;
+		
+			/* Write a setpalette function for your machine, then
+			   you can add something similar here.  These are
+			   grouped by classes of video chipsets.  Some of this
+			   information is from the VideoToolbox "Bugs" web
+			   page at
+			   http://rajsky.psych.nyu.edu/Tips/VideoBugs.html */
+
+			/* Assorted weirdos */
+			/* We think this may be like the LC II */
+		case MAC_MODEL_LC:
+			if (vidtest) {
+				macfb_setpalette = v8_brazil_setpalette;
+				macfb_defined.activate = FB_ACTIVATE_NOW;
+				v8_brazil_cmap_regs =
+					ioremap(DAC_BASE, 0x1000);
+			}
+			strcpy( fb_info.modename, "LC built-in" );
+			break;
+			/* We think this may be like the LC II */
+		case MAC_MODEL_CCL:
+			if (vidtest) {
+				macfb_setpalette = v8_brazil_setpalette;
+				macfb_defined.activate = FB_ACTIVATE_NOW;
+				v8_brazil_cmap_regs =
+					ioremap(DAC_BASE, 0x1000);
+			}
+			strcpy( fb_info.modename, "Color Classic built-in" );
+			break;
+
+			/* And we *do* mean "weirdos" */
+		case MAC_MODEL_TV:
+			strcpy( fb_info.modename, "Mac TV built-in" );
+			break;
+
+			/* These don't have colour, so no need to worry */
+		case MAC_MODEL_SE30:
+		case MAC_MODEL_CLII:
+			strcpy( fb_info.modename, "Monochrome built-in" );
+			break;
+
+			/* Powerbooks are particularly difficult.  Many of
+			   them have separate framebuffers for external and
+			   internal video, which is admittedly pretty cool,
+			   but will be a bit of a headache to support here.
+			   Also, many of them are grayscale, and we don't
+			   really support that. */
+
+			/* Internal is DBLite, External is ViSC */
+		case MAC_MODEL_PB160:
+			strcpy( fb_info.modename, "DBLite built-in" );
+			break;
+
+			/* Internal is TIM, External is ViSC */
+		case MAC_MODEL_PB170:
+		case MAC_MODEL_PB180:
+		case MAC_MODEL_PB180C:
+			strcpy( fb_info.modename, "TIM built-in" );
+			break;
+
+			/* Uses the CSC framebuffer */
+		case MAC_MODEL_PB520:
+			strcpy( fb_info.modename, "CSC built-in" );
+			break;
+		
+		case MAC_MODEL_PB165:
+		case MAC_MODEL_PB165C:
+		case MAC_MODEL_PB140:
+		case MAC_MODEL_PB145:
+		case MAC_MODEL_PB150:
+		case MAC_MODEL_PB190:		
+		default:
+			strcpy( fb_info.modename, "Unknown/Unsupported built-in" );
+			break;
+		}
+	
+	fb_info.changevar  = NULL;
+	fb_info.node       = -1;
+	fb_info.fbops      = &macfb_ops;
+	fb_info.disp       = &disp;
+	fb_info.switch_con = &macfb_switch;
+	fb_info.updatevar  = &macfb_update_var;
+	fb_info.blank      = &macfb_blank;
+	fb_info.flags      = FBINFO_FLAG_DEFAULT;
+	macfb_set_disp(-1);
+	do_install_cmap(0, &fb_info);
+	
 	if (register_framebuffer(&fb_info) < 0)
-	{
 		return;
-	}
 
-	printk("fb%d: %s frame buffer device using %ldK of video memory\n",
-	       GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10);
+	printk("fb%d: %s frame buffer device\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename);
 }
-
-#if 0
-/*
- * These two auxiliary debug functions should go away ASAP. Only usage: 
- * before the console output is up (after head.S come some other crucial
- * setup routines :-) 
- *
- * Now in debug.c ...
- */
-#endif
diff -urN linux-2.2.18-pre15/drivers/video/q40fb.c linux/drivers/video/q40fb.c
--- linux-2.2.18-pre15/drivers/video/q40fb.c	Thu Feb 25 10:02:12 1999
+++ linux/drivers/video/q40fb.c	Mon Dec  3 20:34:12 2001
@@ -6,6 +6,8 @@
 #include <linux/malloc.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
 #include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -15,8 +17,10 @@
 #include <linux/module.h>
 #include <asm/pgtable.h>
 
+#include <video/fbcon.h>
 #include <video/fbcon-cfb16.h>
 
+#define FBIOSETSCROLLMODE   0x4611
 
 #define Q40_PHYS_SCREEN_ADDR 0xFE800000
 static unsigned long q40_screen_addr;
@@ -212,7 +216,7 @@
   blue>>=10;
 
     if (regno < 16) {
-      fbcon_cmap_cfb16[regno] = ((red & 31) <<5) |
+      fbcon_cmap_cfb16[regno] = ((red & 31) <<6) |
 	                         ((green & 31) << 11) |
 	                         (blue & 63);
     }
@@ -275,6 +279,27 @@
 		      unsigned int cmd, unsigned long arg, int con,
 		      struct fb_info *info)
 {
+#if 0
+        unsigned long i;
+	struct display *display;
+
+	if (con>=0)
+	  display = &fb_display[con];
+	else
+	  display = &disp[0];
+
+        if (cmd == FBIOSETSCROLLMODE)
+	  {
+	    i = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long));
+	    if (!i) 
+	      {
+		copy_from_user(&i, (void *)arg, sizeof(unsigned long));
+		display->scrollmode = i;
+	      }
+	    q40_updatescrollmode(display);
+	    return i;
+	  }
+#endif
 	return -EINVAL;
 }
 
@@ -302,6 +327,8 @@
    display->inverse = 0;
    display->line_length = fix.line_length;
 
+   display->scrollmode = SCROLL_YREDRAW;
+
 #ifdef FBCON_HAS_CFB16
    display->dispsw = &fbcon_cfb16;
    disp->dispsw_data = fbcon_cmap_cfb16;
@@ -312,6 +339,9 @@
   
 void q40fb_init(void)
 {
+
+        if ( !MACH_IS_Q40)
+	  return;
 #if 0
         q40_screen_addr = kernel_map(Q40_PHYS_SCREEN_ADDR, 1024*1024,
 					   KERNELMAP_NO_COPYBACK, NULL);
diff -urN linux-2.2.18-pre15/drivers/video/retz3fb.c linux/drivers/video/retz3fb.c
--- linux-2.2.18-pre15/drivers/video/retz3fb.c	Thu Feb 25 10:02:11 1999
+++ linux/drivers/video/retz3fb.c	Mon Dec  3 20:34:12 2001
@@ -109,6 +109,7 @@
 	unsigned long physregs;
 	int currcon;
 	int current_par_valid; /* set to 0 by memset */
+	int blitbusy;
 	struct display disp;
 	struct retz3fb_par current_par;
 	unsigned char color_table [256][3];
@@ -178,11 +179,7 @@
 	"640x480", {		/* 640x480, 8 bpp */
 	    640, 480, 640, 480, 0, 0, 8, 0,
 	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-#if 1
 	    0, 0, -1, -1, FB_ACCEL_NONE, 39722, 48, 16, 33, 10, 96, 2,
-#else
-	    0, 0, -1, -1, FB_ACCELF_TEXT, 38461, 28, 32, 12, 10, 96, 2,
-#endif
 	    FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED
 	}
     },
@@ -346,7 +343,7 @@
 static int get_video_mode(const char *name);
 
 
-/* -------------------- Hardware specific routines -------------------------- */
+/* -------------------- Hardware specific routines ------------------------- */
 
 static unsigned short find_fq(unsigned int freq)
 {
@@ -959,6 +956,21 @@
 }
 
 
+static inline void retz3_busy(struct display *p)
+{
+	struct retz3_fb_info *zinfo = retz3info(p->fb_info);
+	volatile unsigned char *acm = zinfo->base + ACM_OFFSET;
+	unsigned char blt_status;
+
+	if (zinfo->blitbusy) {
+		do{
+			blt_status = *((acm) + (ACM_START_STATUS + 2));
+		}while ((blt_status & 1) == 0);
+		zinfo->blitbusy = 0;
+	}
+}
+
+
 static void retz3_bitblt (struct display *p,
 			  unsigned short srcx, unsigned short srcy,
 			  unsigned short destx, unsigned short desty,
@@ -973,7 +985,6 @@
 	unsigned short mod;
 	unsigned long tmp;
 	unsigned long pat, src, dst;
-	unsigned char blt_status;
 
 	int i, xres_virtual = var->xres_virtual;
 	short bpp = (var->bits_per_pixel & 0xff);
@@ -983,16 +994,7 @@
 
 	tmp = mask | (mask << 16);
 
-#if 0
-	/*
-	 * Check for blitter finished before we start messing with the
-	 * pattern.
-	 */
-	do{
-		blt_status = *(((volatile unsigned char *)acm) +
-			       (ACM_START_STATUS + 2));
-	}while ((blt_status & 1) == 0);
-#endif
+	retz3_busy(p);
 
 	i = 0;
 	do{
@@ -1042,23 +1044,7 @@
 
 	*(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00;
 	*(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x01;
-
-	/*
-	 * No reason to wait for the blitter to finish, it is better
-	 * just to check if it has finished before we use it again.
-	 */
-#if 1
-#if 0
-	while ((*(((volatile unsigned char *)acm) +
-		  (ACM_START_STATUS + 2)) & 1) == 0);
-#else
-	do{
-		blt_status = *(((volatile unsigned char *)acm) +
-			       (ACM_START_STATUS + 2));
-	}
-	while ((blt_status & 1) == 0);
-#endif
-#endif
+	zinfo->blitbusy = 1;
 }
 
 #if 0
@@ -1192,7 +1178,6 @@
 }
 
 
-#if 1
 static void retz3fb_set_disp(int con, struct fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
@@ -1243,7 +1228,6 @@
 		break;
 	}
 }
-#endif
 
 
 /*
@@ -1613,8 +1597,8 @@
  */
 
 #ifdef FBCON_HAS_CFB8
-static void fbcon_retz3_8_bmove(struct display *p, int sy, int sx,
-				int dy, int dx, int height, int width)
+static void retz3_8_bmove(struct display *p, int sy, int sx,
+			  int dy, int dx, int height, int width)
 {
 	int fontwidth = fontwidth(p);
 
@@ -1633,8 +1617,8 @@
 		     0xffff);
 }
 
-static void fbcon_retz3_8_clear(struct vc_data *conp, struct display *p,
-				int sy, int sx, int height, int width)
+static void retz3_8_clear(struct vc_data *conp, struct display *p,
+			  int sy, int sx, int height, int width)
 {
 	unsigned short col;
 	int fontwidth = fontwidth(p);
@@ -1657,9 +1641,42 @@
 		     col);
 }
 
+
+static void retz3_putc(struct vc_data *conp, struct display *p, int c,
+		       int yy, int xx)
+{
+	retz3_busy(p);
+	fbcon_cfb8_putc(conp, p, c, yy, xx);
+}
+
+
+static void retz3_putcs(struct vc_data *conp, struct display *p,
+			const unsigned short *s, int count,
+			int yy, int xx)
+{
+	retz3_busy(p);
+	fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
+}
+
+
+static void retz3_revc(struct display *p, int xx, int yy)
+{
+	retz3_busy(p);
+	fbcon_cfb8_revc(p, xx, yy);
+}
+
+
+static void retz3_clear_margins(struct vc_data* conp, struct display* p,
+				int bottom_only)
+{
+	retz3_busy(p);
+	fbcon_cfb8_clear_margins(conp, p, bottom_only);
+}
+
+
 static struct display_switch fbcon_retz3_8 = {
-    fbcon_cfb8_setup, fbcon_retz3_8_bmove, fbcon_retz3_8_clear,
-    fbcon_cfb8_putc, fbcon_cfb8_putcs, fbcon_cfb8_revc, NULL, NULL,
-    fbcon_cfb8_clear_margins, FONTWIDTH(8)
+    fbcon_cfb8_setup, retz3_8_bmove, retz3_8_clear,
+    retz3_putc, retz3_putcs, retz3_revc, NULL, NULL,
+    retz3_clear_margins, FONTWIDTH(8)
 };
 #endif
diff -urN linux-2.2.18-pre15/fs/Config.in linux/fs/Config.in
--- linux-2.2.18-pre15/fs/Config.in	Mon Dec  3 20:33:36 2001
+++ linux/fs/Config.in	Mon Dec  3 20:34:12 2001
@@ -69,14 +69,15 @@
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'Coda filesystem support (advanced network fs)' CONFIG_CODA_FS
   tristate 'NFS filesystem support' CONFIG_NFS_FS
+  if [ "$CONFIG_NFS_FS" != "n" ]; then
+    bool '   NFS Version 3 filesystem support' CONFIG_NFS_V3
+  fi
   if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
   fi
-  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'NFS server support' CONFIG_NFSD
-  fi
-  if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFSD" != "n" ]; then
-    bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+  tristate 'NFS server support' CONFIG_NFSD
+  if [ "$CONFIG_NFSD" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool '   NFS Version 3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3
   fi
   if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
     define_bool CONFIG_SUNRPC y
@@ -89,9 +90,6 @@
       define_bool CONFIG_SUNRPC n
       define_bool CONFIG_LOCKD n
     fi
-  fi
-  if [ "$CONFIG_NFS_FS" != "n" -o "$CONFIG_NFSD" != "n" ]; then
-    bool '   NFS Version 3' CONFIG_NFS_V3
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS
   if [ "$CONFIG_SMB_FS" != "n" ]; then
diff -urN linux-2.2.18-pre15/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.2.18-pre15/fs/ext2/ialloc.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/ext2/ialloc.c	Mon Dec  3 20:34:12 2001
@@ -478,8 +478,14 @@
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
 		inode->i_flags |= MS_SYNCHRONOUS;
 	insert_inode_hash(inode);
+	/* 
+	 *   dhXXX:
+	 *  To be really picky we should set i_generation to one more than
+	 *  whatever's on the disk, to ensure a monotonic advance of 
+	 *  generation for NFS.  But the odds of duplicating the last igen 
+	 *  are only 1 in 2^32...
+	 */
 	inode->i_generation = inode_generation_count++;
-	inode->u.ext2_i.i_version = inode->i_generation;
 	mark_inode_dirty(inode);
 
 	unlock_super (sb);
diff -urN linux-2.2.18-pre15/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.2.18-pre15/fs/ext2/inode.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/ext2/inode.c	Mon Dec  3 20:34:12 2001
@@ -47,14 +47,14 @@
  */
 void ext2_delete_inode (struct inode * inode)
 {
-	if (inode->i_ino == EXT2_ACL_IDX_INO ||
+	if (is_bad_inode(inode) ||
+	    inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		return;
 	inode->u.ext2_i.i_dtime	= CURRENT_TIME;
-	/* When we delete an inode, we increment its i_version. If it
-	   is ever read in from disk again, it will have a different
-	   i_version. */
-	inode->u.ext2_i.i_version++;
+	/* When we delete an inode, we increment its i_generation.
+	   If it is read in from disk again, the generation will differ. */
+	inode->i_generation++;
 	mark_inode_dirty(inode);
 	ext2_update_inode(inode, IS_SYNC(inode));
 	inode->i_size = 0;
@@ -512,9 +512,20 @@
 	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);
+	/* 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)) {
+		/* this inode is deleted */
+		brelse (bh);
+		goto bad_inode;
+	}
 	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 = ++global_event;
+	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
 	inode->u.ext2_i.i_new_inode = 0;
 	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);
@@ -536,8 +547,6 @@
 			<< 32;
 #endif
 	}
-	inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
-	inode->i_generation = inode->u.ext2_i.i_version;
 	inode->u.ext2_i.i_block_group = block_group;
 	inode->u.ext2_i.i_next_alloc_block = 0;
 	inode->u.ext2_i.i_next_alloc_goal = 0;
@@ -648,6 +657,7 @@
 	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_generation = cpu_to_le32(inode->i_generation);
 	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);
@@ -664,7 +674,6 @@
 		raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
 #endif
 	}
-	raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
 	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++)
diff -urN linux-2.2.18-pre15/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- linux-2.2.18-pre15/fs/ext2/ioctl.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/ext2/ioctl.c	Mon Dec  3 20:34:12 2001
@@ -68,15 +68,14 @@
 		mark_inode_dirty(inode);
 		return 0;
 	case EXT2_IOC_GETVERSION:
-		return put_user(inode->u.ext2_i.i_version, (int *) arg);
+		return put_user(inode->i_generation, (int *) arg);
 	case EXT2_IOC_SETVERSION:
 		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
 			return -EPERM;
 		if (IS_RDONLY(inode))
 			return -EROFS;
-		if (get_user(inode->u.ext2_i.i_version, (int *) arg))
+		if (get_user(inode->i_generation, (int *) arg))
 			return -EFAULT;	
-		inode->i_generation = inode->u.ext2_i.i_version;
 		inode->i_ctime = CURRENT_TIME;
 		mark_inode_dirty(inode);
 		return 0;
diff -urN linux-2.2.18-pre15/fs/lockd/Makefile linux/fs/lockd/Makefile
--- linux-2.2.18-pre15/fs/lockd/Makefile	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/Makefile	Mon Dec  3 20:34:12 2001
@@ -9,11 +9,11 @@
 
 O_TARGET := lockd.o
 O_OBJS   := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
-	    svcproc.o svcsubs.o mon.o xdr.o
+	    svcproc.o svcsubs.o mon.o xdr.o 
 
-ifdef CONFIG_NFS_V3
-  O_OBJS += xdr4.o
-endif
+#ifdef CONFIG_NFS_V3
+  O_OBJS += xdr4.o svc4proc.o
+#endif
 
 OX_OBJS  := lockd_syms.o
 M_OBJS   := $(O_TARGET)
diff -urN linux-2.2.18-pre15/fs/lockd/lockd_syms.c linux/fs/lockd/lockd_syms.c
--- linux-2.2.18-pre15/fs/lockd/lockd_syms.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/lockd/lockd_syms.c	Mon Dec  3 20:34:12 2001
@@ -23,6 +23,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/syscall.h>
 
 /* Start/stop the daemon */
@@ -40,5 +41,18 @@
 /* Configuration at insmod time */
 EXPORT_SYMBOL(nlmsvc_grace_period);
 EXPORT_SYMBOL(nlmsvc_timeout);
+
+/* NLM4 exported symbols */ 
+EXPORT_SYMBOL(nlm4_lck_denied_grace_period);
+EXPORT_SYMBOL(nlm4_lck_denied);
+EXPORT_SYMBOL(nlm4_lck_blocked);
+EXPORT_SYMBOL(nlm4_rofs);
+EXPORT_SYMBOL(nlm4_stale_fh);
+EXPORT_SYMBOL(nlm4_granted);
+EXPORT_SYMBOL(nlm4_deadlock);
+EXPORT_SYMBOL(nlm4_failed);
+EXPORT_SYMBOL(nlm4_fbig);
+EXPORT_SYMBOL(nlm4_lck_denied_nolocks);
+
 
 #endif /* CONFIG_MODULES */
diff -urN linux-2.2.18-pre15/fs/lockd/svc.c linux/fs/lockd/svc.c
--- linux-2.2.18-pre15/fs/lockd/svc.c	Mon Sep  4 10:39:22 2000
+++ linux/fs/lockd/svc.c	Mon Dec  3 20:34:12 2001
@@ -230,6 +230,7 @@
 
 	error = -ENOMEM;
 	serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE);
+
 	if (!serv) {
 		printk(KERN_WARNING "lockd_up: create service failed\n");
 		goto out;
@@ -353,7 +354,7 @@
 static struct svc_version	nlmsvc_version3 = {
 	3, 24, nlmsvc_procedures, NULL
 };
-#ifdef CONFIG_NFSD_NFS3
+#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && defined(CONFIG_NFS_V3)
 static struct svc_version	nlmsvc_version4 = {
 	4, 24, nlmsvc_procedures4, NULL
 };
@@ -363,7 +364,7 @@
 	&nlmsvc_version1,
 	NULL,
 	&nlmsvc_version3,
-#ifdef CONFIG_NFSD_NFS3
+#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && defined(CONFIG_NFS_V3)
 	&nlmsvc_version4,
 #endif
 };
@@ -383,6 +384,7 @@
 int
 lockdctl(int cmd, void *opaque_argp, void *opaque_resp)
 {
+#if 0
 	int err;
 
 	MOD_INC_USE_COUNT;
@@ -398,4 +400,17 @@
 	MOD_DEC_USE_COUNT;
 
 	return err;
+#else
+	/*
+	 *  For the moment, unless a real need for locks on NFS root
+	 *  emerges, we revert to automatic lockd start.  But we will leave the
+	 *  manual call machinery in place in case we ever want to go
+	 *  back to it.  I felt a warning was useful here, but many didn't like
+	 *  that, so I'll suppress it.  - dhiggen
+	 */
+#if 0
+	 printk("lockd: note, lockd is automatic in this kernel.  Remove rpc.lockd from any rc scripts.\n"); 
+#endif
+	return (0);
+#endif
 }
diff -urN linux-2.2.18-pre15/fs/lockd/svc4proc.c linux/fs/lockd/svc4proc.c
--- linux-2.2.18-pre15/fs/lockd/svc4proc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svc4proc.c	Mon Dec  3 20:34:12 2001
@@ -0,0 +1,561 @@
+/*
+ * linux/fs/lockd/svc4proc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES 
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/xdr4.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_CLIENT
+
+static u32	nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void	nlm4svc_callback_exit(struct rpc_task *);
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+			struct nlm_host **hostp, struct nlm_file **filp)
+{
+	struct nlm_host		*host = NULL;
+	struct nlm_file		*file = NULL;
+	struct nlm_lock		*lock = &argp->lock;
+	u32			error = 0;
+
+	/* nfsd callbacks must have been installed for this procedure */
+	if (!nlmsvc_ops)
+		return nlm4_lck_denied_nolocks;
+
+	/* Obtain handle for client host */
+	if (rqstp->rq_client == NULL) {
+		printk(KERN_NOTICE
+			"lockd: unauthenticated request from (%08x:%d)\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return nlm4_lck_denied_nolocks;
+	}
+
+	/* Obtain host handle */
+	if (!(host = nlmsvc_lookup_host(rqstp))
+	 || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+		goto no_locks;
+	*hostp = host;
+
+	/* Obtain file pointer. Not used by FREE_ALL call. */
+	if (filp != NULL) {
+		if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+			goto no_locks;
+		*filp = file;
+
+		/* Set up the missing parts of the file_lock structure */
+		lock->fl.fl_file  = &file->f_file;
+		lock->fl.fl_owner = (fl_owner_t) host;
+	}
+
+	return 0;
+
+no_locks:
+	if (host)
+		nlm_release_host(host);
+	/* check the error to see if its a stale fh error */
+ 	if (error)
+		return error;	
+	return nlm4_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlm4svc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	dprintk("lockd: NULL          called\n");
+	return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: TEST4        called\n");
+	resp->cookie = argp->cookie;
+
+	/* Don't accept test requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now check for conflicting locks */
+	resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+
+	dprintk("lockd: TEST4          status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: LOCK          called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+#if 0
+	/* If supplied state doesn't match current state, we assume it's
+	 * an old request that time-warped somehow. Any error return would
+	 * do in this case because it's irrelevant anyway.
+	 *
+	 * NB: We don't retrieve the remote host's state yet.
+	 */
+	if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+		resp->status = nlm4_lck_denied_nolocks;
+	} else
+#endif
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
+					argp->block, &argp->cookie);
+
+	dprintk("lockd: LOCK          status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: CANCEL        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Try to cancel request. */
+	resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+
+	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNLOCK        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to remove the lock */
+	resp->status = nlmsvc_unlock(file, &argp->lock);
+
+	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	resp->cookie = argp->cookie;
+
+	dprintk("lockd: GRANTED       called\n");
+	resp->status = nlmclnt_grant(&argp->lock);
+	dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
+	return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: TEST_MSG      called\n");
+
+	if ((stat = nlm4svc_proc_test(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_TEST_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: LOCK_MSG      called\n");
+
+	if ((stat = nlm4svc_proc_lock(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					       void	       *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: CANCEL_MSG    called\n");
+
+	if ((stat = nlm4svc_proc_cancel(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                               void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: UNLOCK_MSG    called\n");
+
+	if ((stat = nlm4svc_proc_unlock(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                                void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: GRANTED_MSG   called\n");
+
+	if ((stat = nlm4svc_proc_granted(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+	return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+				          struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: SHARE         called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to create the share */
+	resp->status = nlmsvc_share_file(host, file, argp);
+
+	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNSHARE       called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_unshare_file(host, file, argp);
+
+	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlm4svc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	dprintk("lockd: NM_LOCK       called\n");
+
+	argp->monitor = 0;		/* just clean the monitor flag */
+	return nlm4svc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void            *resp)
+{
+	struct nlm_host	*host;
+
+	/* Obtain client */
+	if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL))
+		return rpc_success;
+
+	nlmsvc_free_host_resources(host);
+	nlm_release_host(host);
+	return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+					      void	        *resp)
+{
+	struct sockaddr_in	saddr = rqstp->rq_addr;
+	struct nlm_host		*host;
+
+	dprintk("lockd: SM_NOTIFY     called\n");
+	if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+	 || ntohs(saddr.sin_port) >= 1024) {
+		printk(KERN_WARNING
+			"lockd: rejected NSM callback from %08x:%d\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return rpc_system_err;
+	}
+
+	/* Obtain the host pointer for this NFS server and try to
+	 * reclaim all locks we hold on this server.
+	 */
+	saddr.sin_addr.s_addr = argp->addr;	
+	if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) {
+		nlmclnt_recovery(host, argp->state);
+		nlm_release_host(host);
+	}
+
+	/* If we run on an NFS server, delete all locks held by the client */
+	if (nlmsvc_ops != NULL) {
+		struct svc_client	*clnt;
+		saddr.sin_addr.s_addr = argp->addr;	
+		if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL 
+		 && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+			nlmsvc_free_host_resources(host);
+		}
+		nlm_release_host(host);
+	}
+
+	return rpc_success;
+}
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_rqst	*call;
+
+	if (!(call = nlmclnt_alloc_call()))
+		return rpc_system_err;
+
+	host = nlmclnt_lookup_host(&rqstp->rq_addr,
+				rqstp->rq_prot, rqstp->rq_vers);
+	if (!host) {
+		rpc_free(call);
+		return rpc_system_err;
+	}
+
+	call->a_flags = RPC_TASK_ASYNC;
+	call->a_host  = host;
+	memcpy(&call->a_args, resp, sizeof(*resp));
+
+	if (nlmsvc_async_call(call, proc, nlm4svc_callback_exit) < 0)
+		return rpc_system_err;
+
+	return rpc_success;
+}
+
+static void
+nlm4svc_callback_exit(struct rpc_task *task)
+{
+	struct nlm_rqst	*call = (struct nlm_rqst *) task->tk_calldata;
+
+	if (task->tk_status < 0) {
+		dprintk("lockd: %4d callback failed (errno = %d)\n",
+					task->tk_pid, -task->tk_status);
+	}
+	nlm_release_host(call->a_host);
+	rpc_free(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+
+#define nlm4svc_encode_norep	nlm4svc_encode_void
+#define nlm4svc_decode_norep	nlm4svc_decode_void
+#define nlm4svc_decode_testres	nlm4svc_decode_void
+#define nlm4svc_decode_lockres	nlm4svc_decode_void
+#define nlm4svc_decode_unlockres	nlm4svc_decode_void
+#define nlm4svc_decode_cancelres	nlm4svc_decode_void
+#define nlm4svc_decode_grantedres	nlm4svc_decode_void
+
+#define nlm4svc_proc_none	nlm4svc_proc_null
+#define nlm4svc_proc_test_res	nlm4svc_proc_null
+#define nlm4svc_proc_lock_res	nlm4svc_proc_null
+#define nlm4svc_proc_cancel_res	nlm4svc_proc_null
+#define nlm4svc_proc_unlock_res	nlm4svc_proc_null
+#define nlm4svc_proc_granted_res	nlm4svc_proc_null
+
+struct nlm_void			{ int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest)	\
+ { (svc_procfunc) nlm4svc_proc_##name,	\
+   (kxdrproc_t) nlm4svc_decode_##xargt,	\
+   (kxdrproc_t) nlm4svc_encode_##xrest,	\
+   NULL,				\
+   sizeof(struct nlm_##argt),		\
+   sizeof(struct nlm_##rest),		\
+   0,					\
+   0					\
+ }
+struct svc_procedure		nlmsvc_procedures4[] = {
+  PROC(null,		void,		void,		void,	void),
+  PROC(test,		testargs,	testres,	args,	res),
+  PROC(lock,		lockargs,	res,		args,	res),
+  PROC(cancel,		cancargs,	res,		args,	res),
+  PROC(unlock,		unlockargs,	res,		args,	res),
+  PROC(granted,		testargs,	res,		args,	res),
+  PROC(test_msg,	testargs,	norep,		args,	void),
+  PROC(lock_msg,	lockargs,	norep,		args,	void),
+  PROC(cancel_msg,	cancargs,	norep,		args,	void),
+  PROC(unlock_msg,	unlockargs,	norep,		args,	void),
+  PROC(granted_msg,	testargs,	norep,		args,	void),
+  PROC(test_res,	testres,	norep,		res,	void),
+  PROC(lock_res,	lockres,	norep,		res,	void),
+  PROC(cancel_res,	cancelres,	norep,		res,	void),
+  PROC(unlock_res,	unlockres,	norep,		res,	void),
+  PROC(granted_res,	grantedres,	norep,		res,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(share,		shareargs,	shareres,	args,	res),
+  PROC(unshare,		shareargs,	shareres,	args,	res),
+  PROC(nm_lock,		lockargs,	res,		args,	res),
+  PROC(free_all,	notify,		void,		args,	void),
+
+  /* statd callback */
+  PROC(sm_notify,	reboot,		void,		reboot,	void),
+};
diff -urN linux-2.2.18-pre15/fs/lockd/svclock.c linux/fs/lockd/svclock.c
--- linux-2.2.18-pre15/fs/lockd/svclock.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/svclock.c	Mon Dec  3 20:34:12 2001
@@ -26,6 +26,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/nlm.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 
 
@@ -98,9 +99,10 @@
 				lock->fl.fl_end, lock->fl.fl_type);
 	for (head = &nlm_blocked; (block = *head); head = &block->b_next) {
 		fl = &block->b_call.a_args.lock.fl;
-		dprintk("       check f=%p pd=%d %ld-%ld ty=%d\n",
+		dprintk("lockd: check f=%p pd=%d %ld-%ld ty=%d cookie=%x\n",
 				block->b_file, fl->fl_pid, fl->fl_start,
-				fl->fl_end, fl->fl_type);
+				fl->fl_end, fl->fl_type, 
+				*(u32 *)(&block->b_call.a_args.cookie.data));
 		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
 			if (remove)
 				*head = block->b_next;
@@ -129,6 +131,8 @@
 	struct nlm_block *block;
 
 	for (block = nlm_blocked; block; block = block->b_next) {
+		dprintk("cookie: head of blocked queue %p, block %p\n", 
+			nlm_blocked, block);
 		if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie))
 			break;
 	}
@@ -280,6 +284,7 @@
 {
 	struct file_lock	*conflock;
 	struct nlm_block	*block;
+	struct inode            *inode = file->f_file.f_dentry->d_inode;
 	int			error;
 
 	dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n",
@@ -289,6 +294,10 @@
 				lock->fl.fl_start,
 				lock->fl.fl_end,
 				wait);
+	
+	/* Checking for read only file system */
+	if (IS_RDONLY(inode))
+		return nlm4_rofs;
 
 	/* Lock file against concurrent access */
 	down(&file->f_sema);
@@ -301,7 +310,7 @@
 again:
 	if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
 		error = posix_lock_file(&file->f_file, &lock->fl, 0);
-
+		
 		if (block)
 			nlmsvc_delete_block(block, 0);
 		up(&file->f_sema);
@@ -309,18 +318,19 @@
 		dprintk("lockd: posix_lock_file returned %d\n", -error);
 		switch(-error) {
 		case 0:
-			return nlm_granted;
-		case EDEADLK:			/* no applicable NLM status */
+			return nlm4_granted;
+		case EDEADLK:		
+			return nlm4_deadlock;
 		case EAGAIN:
-			return nlm_lck_denied;
+			return nlm4_lck_denied;
 		default:			/* includes ENOLCK */
-			return nlm_lck_denied_nolocks;
+			return nlm4_lck_denied_nolocks;
 		}
 	}
 
 	if (!wait) {
 		up(&file->f_sema);
-		return nlm_lck_denied;
+		return nlm4_lck_denied;
 	}
 
 	/* If we don't have a block, create and initialize it. Then
@@ -328,7 +338,7 @@
 	if (block == NULL) {
 		dprintk("lockd: blocking on this lock (allocating).\n");
 		if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
-			return nlm_lck_denied_nolocks;
+			return nlm4_lck_denied_nolocks;
 		goto again;
 	}
 
@@ -343,7 +353,7 @@
 	}
 
 	up(&file->f_sema);
-	return nlm_lck_blocked;
+	return nlm4_lck_blocked;
 }
 
 /*
@@ -364,14 +374,15 @@
 
 	if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
 		dprintk("lockd: conflicting lock(ty=%d, %ld-%ld)\n",
-				fl->fl_type, fl->fl_start, fl->fl_end);
+				fl->fl_type, fl->fl_start, fl->fl_end );
+
 		conflock->caller = "somehost";	/* FIXME */
 		conflock->oh.len = 0;		/* don't return OH info */
 		conflock->fl = *fl;
-		return nlm_lck_denied;
+		return nlm4_lck_denied;
 	}
 
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -399,7 +410,7 @@
 	lock->fl.fl_type = F_UNLCK;
 	error = posix_lock_file(&file->f_file, &lock->fl, 0);
 
-	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
+	return (error < 0)? nlm4_lck_denied_nolocks : nlm4_granted;
 }
 
 /*
@@ -425,7 +436,7 @@
 	if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
 		nlmsvc_delete_block(block, 1);
 	up(&file->f_sema);
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -541,6 +552,8 @@
 	unsigned long		timeout;
 
 	dprintk("lockd: GRANT_MSG RPC callback\n");
+	dprintk("callback: looking for cookie %x \n", 
+		*(u32 *)(call->a_args.cookie.data));
 	if (!(block = nlmsvc_find_block(&call->a_args.cookie))) {
 		dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data));
 		return;
@@ -616,7 +629,7 @@
 	dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
 			nlm_blocked,
 			nlm_blocked? nlm_blocked->b_when : 0);
-	while ((block = nlm_blocked) && block->b_when < jiffies) {
+	while ((block = nlm_blocked) && block->b_when <= jiffies) {
 		dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
 			block, block->b_when, block->b_done);
 		if (block->b_done)
@@ -627,6 +640,5 @@
 
 	if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
 		return (block->b_when - jiffies);
-
 	return MAX_SCHEDULE_TIMEOUT;
 }
diff -urN linux-2.2.18-pre15/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c
--- linux-2.2.18-pre15/fs/lockd/svcproc.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/svcproc.c	Mon Dec  3 20:34:12 2001
@@ -14,6 +14,8 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfsd/nfsd.h>
+#include <linux/lockd/xdr.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 #include <linux/lockd/sm_inter.h>
@@ -23,6 +25,7 @@
 
 static u32	nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *);
 static void	nlmsvc_callback_exit(struct rpc_task *);
+static u32	cast_to_nlm(u32, u32);
 
 /*
  * Obtain client and file from arguments
@@ -93,6 +96,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: TEST          called\n");
 	resp->cookie = argp->cookie;
@@ -108,9 +112,12 @@
 		return rpc_success;
 
 	/* Now check for conflicting locks */
-	resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+	status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+	dprintk("test: status before %d\n", ntohl(status));
+	resp->status = cast_to_nlm(status, rqstp->rq_vers); 
 
-	dprintk("lockd: TEST          status %d\n", ntohl(resp->status));
+	dprintk("lockd: TEST          status %d vers %d\n", 
+		ntohl(resp->status), rqstp->rq_vers);
 	nlm_release_host(host);
 	nlm_release_file(file);
 	return rpc_success;
@@ -122,6 +129,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: LOCK          called\n");
 
@@ -150,8 +158,9 @@
 #endif
 
 	/* Now try to lock the file */
-	resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
-					argp->block, &argp->cookie);
+	status = nlmsvc_lock(rqstp, file, &argp->lock, 
+				argp->block, &argp->cookie);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: LOCK          status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -165,6 +174,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: CANCEL        called\n");
 
@@ -181,7 +191,8 @@
 		return rpc_success;
 
 	/* Try to cancel request. */
-	resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+	status = nlmsvc_cancel_blocked(file, &argp->lock);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -198,6 +209,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: UNLOCK        called\n");
 
@@ -214,7 +226,8 @@
 		return rpc_success;
 
 	/* Now try to remove the lock */
-	resp->status = nlmsvc_unlock(file, &argp->lock);
+	status = nlmsvc_unlock(file, &argp->lock);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);	
 
 	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -322,6 +335,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: SHARE         called\n");
 
@@ -338,7 +352,8 @@
 		return rpc_success;
 
 	/* Now try to create the share */
-	resp->status = nlmsvc_share_file(host, file, argp);
+	status = nlmsvc_share_file(host, file, argp);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -355,6 +370,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: UNSHARE       called\n");
 
@@ -371,7 +387,8 @@
 		return rpc_success;
 
 	/* Now try to lock the file */
-	resp->status = nlmsvc_unshare_file(host, file, argp);
+	status = nlmsvc_unshare_file(host, file, argp);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -495,6 +512,27 @@
 	kfree(call);
 }
 
+static u32
+cast_to_nlm(u32 status, u32 vers)
+{
+
+	if (vers != 4){
+		switch(ntohl(status)){
+		case NLM_LCK_GRANTED:
+		case NLM_LCK_DENIED:
+		case NLM_LCK_DENIED_NOLOCKS:
+		case NLM_LCK_BLOCKED:
+		case NLM_LCK_DENIED_GRACE_PERIOD:
+			break;
+		default:
+			status = NLM_LCK_DENIED_NOLOCKS;
+		}
+	}
+
+	return (status);
+	
+}
+	
 /*
  * NLM Server procedures.
  */
diff -urN linux-2.2.18-pre15/fs/lockd/svcshare.c linux/fs/lockd/svcshare.c
--- linux-2.2.18-pre15/fs/lockd/svcshare.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/svcshare.c	Mon Dec  3 20:34:12 2001
@@ -12,6 +12,7 @@
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 
@@ -35,13 +36,13 @@
 			goto update;
 		if ((argp->fsm_access & share->s_mode)
 		 || (argp->fsm_mode   & share->s_access ))
-			return nlm_lck_denied;
+			return nlm4_lck_denied;
 	}
 
 	share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len,
 						GFP_KERNEL);
 	if (share == NULL)
-		return nlm_lck_denied_nolocks;
+		return nlm4_lck_denied_nolocks;
 
 	/* Copy owner handle */
 	ohdata = (u8 *) (share + 1);
@@ -58,7 +59,7 @@
 update:
 	share->s_access = argp->fsm_access;
 	share->s_mode   = argp->fsm_mode;
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -75,13 +76,13 @@
 		if (share->s_host == host && nlm_cmp_owner(share, oh)) {
 			*shpp = share->s_next;
 			kfree(share);
-			return nlm_granted;
+			return nlm4_granted;
 		}
 	}
 
 	/* X/Open spec says return success even if there was no
 	 * corresponding share. */
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
diff -urN linux-2.2.18-pre15/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- linux-2.2.18-pre15/fs/lockd/svcsubs.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/svcsubs.c	Mon Dec  3 20:34:12 2001
@@ -13,6 +13,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfsd/nfsfh.h>
 #include <linux/nfsd/export.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 #include <linux/lockd/sm_inter.h>
@@ -69,7 +70,7 @@
 
 	dprintk("lockd: creating file for %s/%u\n",
 		kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino);
-	nfserr = nlm_lck_denied_nolocks;
+	nfserr = nlm4_lck_denied_nolocks;
 	file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL);
 	if (!file)
 		goto out_unlock;
@@ -104,7 +105,10 @@
 
 out_free:
 	kfree(file);
-	nfserr = nlm_lck_denied;
+        if (nfserr == 1)
+                nfserr = nlm4_stale_fh;
+        else
+		nfserr = nlm4_lck_denied;
 	goto out_unlock;
 }
 
diff -urN linux-2.2.18-pre15/fs/lockd/xdr.c linux/fs/lockd/xdr.c
--- linux-2.2.18-pre15/fs/lockd/xdr.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/xdr.c	Mon Dec  3 20:34:12 2001
@@ -17,6 +17,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/sm_inter.h>
 
@@ -53,6 +54,17 @@
 	nlm_lck_blocked = htonl(NLM_LCK_BLOCKED);
 	nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
 
+        nlm4_granted = htonl(NLM_LCK_GRANTED);
+        nlm4_lck_denied = htonl(NLM_LCK_DENIED);
+        nlm4_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS);
+        nlm4_lck_blocked = htonl(NLM_LCK_BLOCKED);
+        nlm4_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
+	nlm4_deadlock = htonl(NLM_DEADLCK);
+	nlm4_rofs = htonl(NLM_ROFS);
+	nlm4_stale_fh = htonl(NLM_STALE_FH);
+	nlm4_fbig = htonl(NLM_FBIG);
+	nlm4_failed = htonl(NLM_FAILED);
+
 	inited = 1;
 
 	nlm_register_stats();
@@ -561,7 +573,7 @@
       (kxdrproc_t) nlmclt_encode_##argtype,			\
       (kxdrproc_t) nlmclt_decode_##restype,			\
       MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2,		\
-      0								\
+      0                                                         \
     }
 
 static struct rpc_procinfo	nlm_procedures[] = {
diff -urN linux-2.2.18-pre15/fs/lockd/xdr4.c linux/fs/lockd/xdr4.c
--- linux-2.2.18-pre15/fs/lockd/xdr4.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/lockd/xdr4.c	Mon Dec  3 20:34:12 2001
@@ -1,5 +1,5 @@
 /*
- * linux/fs/lockd/xdr.c
+ * linux/fs/lockd/xdr4.c
  *
  * XDR support for lockd and the lock client.
  *
@@ -27,6 +27,10 @@
 
 #define QUADLEN(len)		(((len) + 3) >> 2)
 
+u32      nlm4_granted, nlm4_lck_denied, nlm4_lck_denied_nolocks,
+         nlm4_lck_blocked, nlm4_lck_denied_grace_period, nlm4_deadlock,
+         nlm4_rofs, nlm4_stale_fh, nlm4_fbig, nlm4_failed;
+
 
 typedef struct nlm_args	nlm_args;
 
@@ -172,11 +176,13 @@
 static u32 *
 nlm4_encode_testres(u32 *p, struct nlm_res *resp)
 {
+
+	dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
 	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
 		return 0;
 	*p++ = resp->status;
 
-	if (resp->status == nlm_lck_denied) {
+	if (resp->status == nlm4_lck_denied) {
 		struct file_lock	*fl = &resp->lock.fl;
 
 		*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
@@ -191,8 +197,12 @@
 			p = xdr_encode_hyper(p, 0);
 		else
 			p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1);
+		dprintk("xdr: encode_testres (status %d pid %d type %d start %ld end %ld)\n", resp->status, fl->fl_pid, fl->fl_type, fl->fl_start,  fl->fl_end);
+
+
 	}
 
+	dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
 	return p;
 }
 
@@ -542,7 +552,7 @@
       (kxdrproc_t) nlm4clt_encode_##argtype,			\
       (kxdrproc_t) nlm4clt_decode_##restype,			\
       MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2,	\
-      0								\
+      0                                                         \
     }
 
 static struct rpc_procinfo	nlm4_procedures[] = {
diff -urN linux-2.2.18-pre15/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux-2.2.18-pre15/fs/nfs/inode.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/nfs/inode.c	Mon Dec  3 20:34:12 2001
@@ -177,10 +177,9 @@
 
 	nfs_reqlist_free(server);
 
-#if 0
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
-#endif
+
 	rpciod_down();		/* release rpciod */
 
 	kfree(server->hostname);
@@ -507,11 +506,10 @@
 	/* We're airborne */
 	unlock_super(sb);
 
-#if 0
 	/* Check whether to start the lockd process */
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_up();
-#endif
+
 	return sb;
 
 	/* Yargs. It didn't work out. */
diff -urN linux-2.2.18-pre15/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- linux-2.2.18-pre15/fs/nfs/nfsroot.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/nfs/nfsroot.c	Mon Dec  3 20:34:12 2001
@@ -243,9 +243,12 @@
 	memset(&nfs_data, 0, sizeof(nfs_data));
 	nfs_port          = -1;
 	nfs_data.version  = NFS_MOUNT_VERSION;
-	/* It is ok to have lockd in nfs root since it will be started
-	   later manually in the rc script. */
-	nfs_data.flags    = 0;
+	/*
+	 *  dhiggen: nobody has yet demonstrated a convincing need for NFS root
+	 *  locking, so I am reverting to automatic lockd start and disabling
+	 *  NLM locking on an NFS root.
+	 */
+	nfs_data.flags    = NFS_MOUNT_NONLM;
 	nfs_data.timeo    = 7;
 	nfs_data.retrans  = 3;
 	nfs_data.acregmin = 3;
diff -urN linux-2.2.18-pre15/fs/nfsd/Makefile linux/fs/nfsd/Makefile
--- linux-2.2.18-pre15/fs/nfsd/Makefile	Tue Dec 29 11:42:25 1998
+++ linux/fs/nfsd/Makefile	Mon Dec  3 20:34:12 2001
@@ -11,6 +11,9 @@
 O_OBJS   := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
 	    export.o auth.o lockd.o nfscache.o nfsxdr.o \
 	    stats.o
+ifdef CONFIG_NFSD_V3
+  O_OBJS += nfs3proc.o nfs3xdr.o
+endif
 
 M_OBJS   := $(O_TARGET)
 
diff -urN linux-2.2.18-pre15/fs/nfsd/export.c linux/fs/nfsd/export.c
--- linux-2.2.18-pre15/fs/nfsd/export.c	Tue Oct 26 17:53:42 1999
+++ linux/fs/nfsd/export.c	Mon Dec  3 20:34:12 2001
@@ -105,20 +105,6 @@
 	return exp;
 }
 
-/*
- * Check whether there are any exports for a device.
- */
-static int
-exp_device_in_use(kdev_t dev)
-{
-	struct svc_client *clp;
-
-	for (clp = clients; clp; clp = clp->cl_next) {
-		if (exp_find(clp, dev))
-			return 1;
-	}
-	return 0;
-}
 
 /*
  * Look up the device of the parent fs.
@@ -168,9 +154,8 @@
 					}
 				} while (NULL != (exp = exp->ex_next));
 		} while (nfsd_parentdev(&xdev));
-		if (xdentry == xdentry->d_parent) {
+		if (IS_ROOT(xdentry))
 			break;
-		}
 	} while ((xdentry = xdentry->d_parent));
 	exp = NULL;
 out:
@@ -204,7 +189,7 @@
 #endif
 						goto out;
 					}
-					if (ndentry == ndentry->d_parent)
+					if (IS_ROOT(ndentry))
 						break;
 				}
 		} while (NULL != (exp = exp->ex_next));
@@ -287,6 +272,12 @@
 		goto finish;
 
 	err = -EINVAL;
+	if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) ||
+	    inode->i_sb->s_op->read_inode == NULL) {
+		dprintk("exp_export: export of invalid fs type.\n");
+		goto finish;
+	}
+
 	if ((parent = exp_child(clp, dev, dentry)) != NULL) {
 		dprintk("exp_export: export not valid (Rule 3).\n");
 		goto finish;
@@ -366,16 +357,6 @@
 				exp->ex_parent = unexp->ex_parent;
 	}
 
-	/*
-	 * Check whether this is the last export for this device,
-	 * and if so flush any cached dentries.
-	 */
-	if (!exp_device_in_use(unexp->ex_dev)) {
-printk("exp_do_unexport: %s last use, flushing cache\n",
-	kdevname(unexp->ex_dev));
-		nfsd_fh_flush(unexp->ex_dev);
-	}
-
 	dentry = unexp->ex_dentry;
 	inode = dentry->d_inode;
 	if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
@@ -628,7 +609,9 @@
 	{ NFSEXP_UIDMAP, {"uidmap", ""}},
 	{ NFSEXP_KERBEROS, { "kerberos", ""}},
 	{ NFSEXP_SUNSECURE, { "sunsecure", ""}},
-	{ NFSEXP_CROSSMNT, {"crossmnt", ""}},
+	{ NFSEXP_CROSSMNT, {"nohide", ""}},
+	{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
+	{ NFSEXP_NOAUTHNLM, {"no_auth_nlm", ""}},
 	{ 0, {"", ""}}
 };
 
diff -urN linux-2.2.18-pre15/fs/nfsd/lockd.c linux/fs/nfsd/lockd.c
--- linux-2.2.18-pre15/fs/nfsd/lockd.c	Mon Aug 31 11:03:38 1998
+++ linux/fs/nfsd/lockd.c	Mon Dec  3 20:34:12 2001
@@ -30,11 +30,21 @@
 	fh.fh_handle = *f;
 	fh.fh_export = NULL;
 
-	nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
+	nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp);
 	if (!nfserr)
 		dget(filp->f_dentry);
 	fh_put(&fh);
-	return nfserr;
+        /* nlm and nfsd don't share error codes.
+         * we invent: 0 = no error
+         *            1 = stale file handle
+         *            2 = other error
+         */
+        if (nfserr == 0)
+                return 0;
+        else if (nfserr == nfserr_stale)
+                return 1;
+        else return 2;
+
 }
 
 static void
diff -urN linux-2.2.18-pre15/fs/nfsd/nfs3proc.c linux/fs/nfsd/nfs3proc.c
--- linux-2.2.18-pre15/fs/nfsd/nfs3proc.c	Mon Apr 12 10:07:36 1999
+++ linux/fs/nfsd/nfs3proc.c	Mon Dec  3 20:34:12 2001
@@ -3,7 +3,7 @@
  *
  * Process version 3 NFS requests.
  *
- * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/linkage.h>
@@ -18,19 +18,33 @@
 #include <linux/version.h>
 #include <linux/unistd.h>
 #include <linux/malloc.h>
+#include <linux/major.h>
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/cache.h>
 #include <linux/nfsd/xdr3.h>
-
-typedef struct svc_rqst	svc_rqst;
-typedef struct svc_buf	svc_buf;
+#include <linux/nfs3.h>
+#include <linux/ext2_fs.h>
 
 #define NFSDDBG_FACILITY		NFSDDBG_PROC
 
 #define RETURN(st)	{ resp->status = (st); return (st); }
 
+static int	nfs3_ftypes[] = {
+	0,			/* NF3NON */
+	S_IFREG,		/* NF3REG */
+	S_IFDIR,		/* NF3DIR */
+	S_IFBLK,		/* NF3BLK */
+	S_IFCHR,		/* NF3CHR */
+	S_IFLNK,		/* NF3LNK */
+	S_IFSOCK,		/* NF3SOCK */
+	S_IFIFO,		/* NF3FIFO */
+};
+
+/*
+ * Reserve room in the send buffer
+ */
 static void
 svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
 {
@@ -38,6 +52,9 @@
 	*len = buf->buflen - buf->len - nr;
 }
 
+/*
+ * NULL call.
+ */
 static int
 nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
 {
@@ -46,7 +63,6 @@
 
 /*
  * Get a file's attributes
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
@@ -54,18 +70,17 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: GETATTR  %x/%ld\n",
+	dprintk("nfsd: GETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
 	RETURN(nfserr);
 }
 
 /*
  * Set a file's attributes
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp,
@@ -73,31 +88,30 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: SETATTR  %x/%ld\n",
+	dprintk("nfsd: SETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs);
 	RETURN(nfserr);
 }
 
 /*
  * Look up a path name component
- * N.B. After this call _both_ resp->dirfh and resp->fh need an fh_put
  */
 static int
 nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-					  struct nfsd3_lookupres *resp)
+					  struct nfsd3_diropres  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: LOOKUP   %x/%ld %s\n",
+	dprintk("nfsd: LOOKUP(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	resp->dirfh = argp->fh;
+	fh_copy(&resp->dirfh, &argp->fh);
 	nfserr = nfsd_lookup(rqstp, &resp->dirfh,
 				    argp->name,
 				    argp->len,
@@ -109,12 +123,20 @@
  * Check file access
  */
 static int
-nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd_fhandle   *argp,
+nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
 					  struct nfsd3_accessres *resp)
 {
-	/* to be done */
-	resp->fh = argp->fh;
-	return nfserr_notsupp;
+	int	nfserr;
+
+	dprintk("nfsd: ACCESS(3)   %x/%ld 0x%x\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->access);
+
+	fh_copy(&resp->fh, &argp->fh);
+	resp->access = argp->access;
+	nfserr = nfsd_access(rqstp, &resp->fh, &resp->access);
+	RETURN(nfserr);
 }
 
 /*
@@ -127,23 +149,23 @@
 	u32		*path;
 	int		dummy, nfserr;
 
-	dprintk("nfsd: READLINK %x/%ld\n",
+	dprintk("nfsd: READLINK(3) %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
 	/* Reserve room for status, post_op_attr, and path length */
-	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 1 + 22 + 1);
+	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy,
+				1 + NFS3_POST_OP_ATTR_WORDS + 1);
 
 	/* Read the symlink. */
+	fh_copy(&resp->fh, &argp->fh);
 	resp->len = NFS3_MAXPATHLEN;
-	nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
-	fh_put(&argp->fh);
+	nfserr = nfsd_readlink(rqstp, &resp->fh, (char *) path, &resp->len);
 	RETURN(nfserr);
 }
 
 /*
  * Read a portion of a file.
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
@@ -152,9 +174,9 @@
 	u32 *	buffer;
 	int	nfserr, avail;
 
-	dprintk("nfsd: READ %x/%ld %lu bytes at %lu\n",
+	dprintk("nfsd: READ(3) %x/%ld %lu bytes at %lu\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				(unsigned long) argp->count,
 				(unsigned long) argp->offset);
 
@@ -162,30 +184,29 @@
 	 * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
 	 * + 1 (xdr opaque byte count) = 26
 	 */
-	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 26);
-
-	if ((avail << 2) < argp->count) {
-		printk(KERN_NOTICE
-			"oversized read request from %08lx:%d (%d bytes)\n",
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port),
-				argp->count);
-		argp->count = avail;
-	}
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail,
+			1 + NFS3_POST_OP_ATTR_WORDS + 3);
 
 	resp->count = argp->count;
-	resp->fh    = argp->fh;
+	if ((avail << 2) < resp->count)
+		resp->count = avail << 2;
+
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_read(rqstp, &resp->fh,
 				  argp->offset,
 				  (char *) buffer,
 				  &resp->count);
+	if (nfserr == 0) {
+		struct inode	*inode = resp->fh.fh_dentry->d_inode;
+
+		resp->eof = (argp->offset + resp->count) >= inode->i_size;
+	}
 
 	RETURN(nfserr);
 }
 
 /*
  * Write data to a file
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
@@ -193,19 +214,21 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: WRITE    %x/%ld %d bytes at %ld\n",
+	dprintk("nfsd: WRITE(3)    %x/%ld %d bytes at %ld%s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->len,
-				(unsigned long) argp->offset);
+				(unsigned long) argp->offset,
+				argp->stable? " stable" : "");
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_write(rqstp, &resp->fh,
 				   argp->offset,
 				   argp->data,
 				   argp->len,
 				   argp->stable);
 	resp->committed = argp->stable;
+	resp->count = argp->count;
 	RETURN(nfserr);
 }
 
@@ -213,20 +236,18 @@
  * With NFSv3, CREATE processing is a lot easier than with NFSv2.
  * At least in theory; we'll see how it fares in practice when the
  * first reports about SunOS compatibility problems start to pour in...
- * N.B. After this call _both_ resp->dirfh and resp->fh need an fh_put
  */
 static int
 nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
-					  struct nfsd3_createres  *resp)
+					  struct nfsd3_diropres   *resp)
 {
 	svc_fh		*dirfhp, *newfhp = NULL;
 	struct iattr	*attr;
-	int		mode;
 	u32		nfserr;
 
-	dprintk("nfsd: CREATE   %x/%ld %s\n",
+	dprintk("nfsd: CREATE(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
 	dirfhp = fh_copy(&resp->dirfh, &argp->fh);
@@ -243,131 +264,114 @@
 	if (!(attr->ia_valid & ATTR_MODE)) { 
 		attr->ia_valid |= ATTR_MODE;
 		attr->ia_mode = S_IFREG;
+	} else {
+		attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG;
 	}
-	mode = attr->ia_mode & ~S_IFMT;
 
 	/* Now create the file and set attributes */
-	nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
-				attr, S_IFREG, 0, newfhp);
+	nfserr = nfsd_create_v3(rqstp, dirfhp, argp->name, argp->len,
+				attr, newfhp,
+				argp->createmode, argp->verf);
 
 	RETURN(nfserr);
 }
 
-/* N.B. Is nfsd3_attrstat * correct for resp?? table says "void" */
+/*
+ * Make directory. This operation is not idempotent.
+ */
 static int
-nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-					  struct nfsd3_attrstat  *resp)
+nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+					 struct nfsd3_diropres   *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: REMOVE   %x/%ld %s\n",
+	dprintk("nfsd: MKDIR(3)    %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	/* Is this correct?? */
-	fh_copy(&resp->fh, &argp->fh);
+	argp->attrs.ia_valid &= ~ATTR_SIZE;
+	fh_copy(&resp->dirfh, &argp->fh);
+	fh_init(&resp->fh);
+	nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+				    &argp->attrs, S_IFDIR, 0, &resp->fh);
 
-	/* Unlink. -S_IFDIR means file must not be a directory */
-	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
-	/* 
-	 * N.B. Should be an fh_put here ... nfsd3_proc_rmdir has one,
-	 * or else as an xdr release function
-	 */
-	fh_put(&resp->fh);
 	RETURN(nfserr);
 }
 
 static int
-nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
-				  	 void		        *resp)
+nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
+					   struct nfsd3_diropres    *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
+	dprintk("nfsd: SYMLINK(3)  %x/%ld %s -> %s\n",
 				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname,
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
+				(long)SVCFH_INO(&argp->ffh),
+				argp->fname, argp->tname);
 
-	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
-				    &argp->tfh, argp->tname, argp->tlen);
-	fh_put(&argp->ffh);
-	fh_put(&argp->tfh);
+	fh_copy(&resp->dirfh, &argp->ffh);
+	fh_init(&resp->fh);
+	nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
+						   argp->tname, argp->tlen,
+						   &resp->fh, &argp->attrs);
 	RETURN(nfserr);
 }
 
+/*
+ * Make socket/fifo/device.
+ */
 static int
-nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
-				void			    *resp)
+nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp,
+					 struct nfsd3_diropres  *resp)
 {
-	int	nfserr;
+	int	nfserr, type;
+	dev_t	rdev = 0;
 
-	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
-
-	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
-				  &argp->ffh);
-	fh_put(&argp->ffh);
-	fh_put(&argp->tfh);
-	RETURN(nfserr);
-}
-
-static int
-nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
-				          void			  *resp)
-{
-	struct svc_fh	newfh;
-	int		nfserr;
+	dprintk("nfsd: MKNOD(3)    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->name);
 
-	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname, argp->tname);
+	fh_copy(&resp->dirfh, &argp->fh);
+	fh_init(&resp->fh);
 
-	memset(&newfh, 0, sizeof(newfh));
+	if (argp->ftype == 0 || argp->ftype >= NF3BAD)
+		return nfserr_inval;
+	if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
+		if ((argp->ftype == NF3CHR && argp->major >= MAX_CHRDEV)
+		    || (argp->ftype == NF3BLK && argp->major >= MAX_BLKDEV)
+		    || argp->minor > 0xFF)
+			return nfserr_inval;
+		rdev = ((argp->major) << 8) | (argp->minor);
+	} else
+		if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO)
+			return nfserr_inval;
 
-	/*
-	 * Create the link, look up new file and set attrs.
-	 */
-	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
-						 argp->tname, argp->tlen,
-						 &newfh);
-	if (!nfserr) {
-		argp->attrs.ia_valid &= ~ATTR_SIZE;
-		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
-	}
+	type = nfs3_ftypes[argp->ftype];
+	nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+				    &argp->attrs, type, rdev, &resp->fh);
 
-	fh_put(&argp->ffh);
-	fh_put(&newfh);
 	RETURN(nfserr);
 }
 
 /*
- * Make directory. This operation is not idempotent.
- * N.B. After this call resp->fh needs an fh_put
+ * Remove file/fifo/socket etc.
  */
 static int
-nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
-					struct nfsd3_diropres   *resp)
+nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+					  struct nfsd3_attrstat  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: MKDIR    %x/%ld %s\n",
+	dprintk("nfsd: REMOVE(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	argp->attrs.ia_valid &= ~ATTR_SIZE;
-	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
-				    &argp->attrs, S_IFDIR, 0, &resp->fh);
-	fh_put(&argp->fh);
+	/* Unlink. -S_IFDIR means file must not be a directory */
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
 	RETURN(nfserr);
 }
 
@@ -376,17 +380,58 @@
  */
 static int
 nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-				 	void		      *resp)
+					 struct nfsd3_attrstat  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: RMDIR    %x/%ld %s\n",
+	dprintk("nfsd: RMDIR(3)    %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
-	fh_put(&argp->fh);
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
+					  struct nfsd3_renameres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RENAME(3)   %x/%ld %s -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				(long)SVCFH_INO(&argp->ffh),
+				argp->fname,
+				SVCFH_DEV(&argp->tfh),
+				(long)SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	fh_copy(&resp->ffh, &argp->ffh);
+	fh_copy(&resp->tfh, &argp->tfh);
+	nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
+				    &resp->tfh, argp->tname, argp->tlen);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
+					struct nfsd3_linkres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LINK(3)     %x/%ld -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				(long)SVCFH_INO(&argp->ffh),
+				SVCFH_DEV(&argp->tfh),
+				(long)SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	fh_copy(&resp->fh,  &argp->ffh);
+	fh_copy(&resp->tfh, &argp->tfh);
+	nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
+				  &resp->fh);
 	RETURN(nfserr);
 }
 
@@ -395,46 +440,85 @@
  */
 static int
 nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
-					  struct nfsd3_readdirres  *resp)
+					   struct nfsd3_readdirres  *resp)
+{
+	u32 *		buffer;
+	int		nfserr, count;
+	unsigned int	want;
+
+	dprintk("nfsd: READDIR(3)  %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->count, (u32) argp->cookie);
+
+	/* Reserve buffer space for status, attributes and verifier */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+				1 + NFS3_POST_OP_ATTR_WORDS + 2);
+
+	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
+	 * client read size */
+	if ((count -= 2) > (want = (argp->count >> 2) - 2))
+		count = want;
+
+	/* Read directory and encode entries on the fly */
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie, 
+					nfs3svc_encode_entry,
+					buffer, &count, argp->verf);
+	memcpy(resp->verf, argp->verf, 8);
+	resp->count = count;
+
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a directory, including file handles and attrs.
+ * For now, we choose to ignore the dircount parameter.
+ */
+static int
+nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
+					       struct nfsd3_readdirres  *resp)
 {
 	u32 *	buffer;
-	int	nfserr, count;
+	int	nfserr, count, want;
 
-	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
+	dprintk("nfsd: READDIR+(3) %x/%ld %d bytes at %d\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->count, argp->cookie);
+				(long)SVCFH_INO(&argp->fh),
+				argp->count, (u32) argp->cookie);
 
-	/* Reserve buffer space for status */
-	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
+	/* Reserve buffer space for status, attributes and verifier */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+				1 + NFS3_POST_OP_ATTR_WORDS + 2);
 
 	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
 	 * client read size */
-	if ((count -= 8) > argp->count)
-		count = argp->count;
+	if ((count -= 2) > (want = argp->count >> 2))
+		count = want;
 
 	/* Read directory and encode entries on the fly */
-	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
-					nfssvc_encode_entry,
-					buffer, &count);
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie, 
+					nfs3svc_encode_entry_plus,
+					buffer, &count, argp->verf);
+	memcpy(resp->verf, argp->verf, 8);
 	resp->count = count;
 
-	fh_put(&argp->fh);
 	RETURN(nfserr);
 }
 
 /*
- * Get file system info
+ * Get file system stats
  */
 static int
-nfsd3_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
-					  struct nfsd3_statfsres *resp)
+nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
+					   struct nfsd3_fsstatres *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: STATFS   %x/%ld\n",
+	dprintk("nfsd: FSSTAT(3)   %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
 	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
 	fh_put(&argp->fh);
@@ -442,104 +526,165 @@
 }
 
 /*
- * NFSv2 Server procedures.
- * Only the results of non-idempotent operations are cached.
+ * Get file system info
  */
-#define nfsd3_proc_none		NULL
-#define nfssvc_encode_void	NULL
-#define nfssvc_decode_void	NULL
-#define nfssvc_release_void	NULL
-struct nfsd3_void { int dummy; };
+static int
+nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
+					   struct nfsd3_fsinfores *resp)
+{
+	int	nfserr;
 
-#define PROC(name, argt, rest, relt, cache)	\
- { (svc_procfunc) nfsd3_proc_##name,	\
-   (kxdrproc_t) nfssvc_decode_##argt,	\
-   (kxdrproc_t) nfssvc_encode_##rest,	\
-   (kxdrproc_t) nfssvc_release_##relt,	\
-   sizeof(struct nfsd3_##argt),		\
-   sizeof(struct nfsd3_##rest),		\
-   0,					\
-   cache				\
- }
-struct svc_procedure		nfsd3_procedures2[18] = {
-  PROC(null,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
-  PROC(setattr,  sattrargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(lookup,	 diropargs,	diropres,	fhandle2,RC_NOCACHE),
-  PROC(readlink, fhandle,	readlinkres,	void,	 RC_NOCACHE),
-  PROC(read,	 readargs,	readres,	fhandle, RC_NOCACHE),
-  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(write,	 writeargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(create,	 createargs,	diropres,	fhandle2,RC_REPLBUFF),
-  PROC(remove,	 diropargs,	void,/* ??*/	void,	 RC_REPLSTAT),
-  PROC(rename,	 renameargs,	void,		void,	 RC_REPLSTAT),
-  PROC(link,	 linkargs,	void,		void,	 RC_REPLSTAT),
-  PROC(symlink,	 symlinkargs,	void,		void,	 RC_REPLSTAT),
-  PROC(mkdir,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
-  PROC(rmdir,	 diropargs,	void,		void,	 RC_REPLSTAT),
-  PROC(readdir,	 readdirargs,	readdirres,	void,	 RC_REPLSTAT),
-  PROC(statfs,	 fhandle,	statfsres,	void,	 RC_NOCACHE),
-};
+	dprintk("nfsd: FSINFO(3)   %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh));
 
+	resp->f_rtmax  = NFSSVC_MAXBLKSIZE;
+	resp->f_rtpref = NFSSVC_MAXBLKSIZE;
+	resp->f_rtmult = PAGE_SIZE;
+	resp->f_wtmax  = NFSSVC_MAXBLKSIZE;
+	resp->f_wtpref = NFSSVC_MAXBLKSIZE;
+	resp->f_wtmult = PAGE_SIZE;
+	resp->f_dtpref = PAGE_SIZE;
+	resp->f_maxfilesize = LONG_MAX;
+	resp->f_properties = NFS3_FSF_DEFAULT;
+
+	nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+	/* Check special features of the file system. May request
+	 * different read/write sizes for file systems known to have
+	 * problems with large blocks */
+	if (nfserr == 0) {
+		struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+		/* Note that we don't care for remote fs's here */
+		if (sb->s_magic == 0x4d44 /* MSDOS_SUPER_MAGIC */) {
+			resp->f_properties = NFS3_FSF_BILLYBOY;
+		}
+	}
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
 
 /*
- * Map errnos to NFS errnos.
+ * Get pathconf info for the specified file
  */
-int
-nfserrno (int errno)
+static int
+nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle      *argp,
+					     struct nfsd3_pathconfres *resp)
 {
-	static struct {
-		int	nfserr;
-		int	syserr;
-	} nfs_errtbl[] = {
-		{ NFS_OK, 0 },
-		{ NFSERR_PERM, EPERM },
-		{ NFSERR_NOENT, ENOENT },
-		{ NFSERR_IO, EIO },
-		{ NFSERR_NXIO, ENXIO },
-		{ NFSERR_ACCES, EACCES },
-		{ NFSERR_EXIST, EEXIST },
-		{ NFSERR_NODEV, ENODEV },
-		{ NFSERR_NOTDIR, ENOTDIR },
-		{ NFSERR_ISDIR, EISDIR },
-		{ NFSERR_INVAL, EINVAL },
-		{ NFSERR_FBIG, EFBIG },
-		{ NFSERR_NOSPC, ENOSPC },
-		{ NFSERR_ROFS, EROFS },
-		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
-		{ NFSERR_NOTEMPTY, ENOTEMPTY },
-#ifdef EDQUOT
-		{ NFSERR_DQUOT, EDQUOT },
-#endif
-		{ NFSERR_STALE, ESTALE },
-		{ NFSERR_WFLUSH, EIO },
-		{ -1, EIO }
-	};
-	int	i;
-
-	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
-		if (nfs_errtbl[i].syserr == errno)
-			return htonl (nfs_errtbl[i].nfserr);
+	int	nfserr;
+
+	dprintk("nfsd: PATHCONF(3) %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh));
+
+	/* Set default pathconf */
+	resp->p_link_max = 255;		/* at least */
+	resp->p_name_max = 255;		/* at least */
+	resp->p_no_trunc = 0;
+	resp->p_chown_restricted = 1;
+	resp->p_case_insensitive = 0;
+	resp->p_case_preserving = 1;
+
+	nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+	if (nfserr == 0) {
+		struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+		/* Note that we don't care for remote fs's here */
+		switch (sb->s_magic) {
+		case EXT2_SUPER_MAGIC:
+			resp->p_link_max = EXT2_LINK_MAX;
+			resp->p_name_max = EXT2_NAME_LEN;
+			break;
+		case 0x4d44:	/* MSDOS_SUPER_MAGIC */
+			resp->p_case_insensitive = 1;
+			resp->p_case_preserving  = 0;
+			break;
+		}
 	}
-	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
-	return nfserr_io;
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
 }
 
-#if 0
-static void
-nfsd3_dump(char *tag, u32 *buf, int len)
+
+/*
+ * Commit a file (range) to stable storage.
+ */
+static int
+nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp,
+					   struct nfsd3_commitres  *resp)
 {
-	int	i;
+	int	nfserr;
 
-	printk(KERN_NOTICE
-		"nfsd: %s (%d words)\n", tag, len);
+	dprintk("nfsd: COMMIT(3)   %x/%ld %d@%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->count,
+				(unsigned long) argp->offset);
 
-	for (i = 0; i < len && i < 32; i += 8)
-		printk(KERN_NOTICE
-			" %08lx %08lx %08lx %08lx"
-			" %08lx %08lx %08lx %08lx\n",
-			buf[i],   buf[i+1], buf[i+2], buf[i+3],
-			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+	if (argp->offset > NFS_OFFSET_MAX)
+		return nfserr_inval;
+
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
+
+	RETURN(nfserr);
 }
-#endif
+
+
+/*
+ * NFSv3 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfs3svc_decode_voidargs		NULL
+#define nfs3svc_release_void		NULL
+#define nfs3svc_decode_fhandleargs	nfs3svc_decode_fhandle
+#define nfs3svc_encode_attrstatres	nfs3svc_encode_attrstat
+#define nfs3svc_encode_wccstatres	nfs3svc_encode_wccstat
+#define nfsd3_mkdirargs			nfsd3_createargs
+#define nfsd3_readdirplusargs		nfsd3_readdirargs
+#define nfsd3_fhandleargs		nfsd_fhandle
+#define nfsd3_fhandleres		nfsd3_attrstat
+#define nfsd3_attrstatres		nfsd3_attrstat
+#define nfsd3_wccstatres		nfsd3_attrstat
+#define nfsd3_createres			nfsd3_diropres
+#define nfsd3_voidres			nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache)	\
+ { (svc_procfunc) nfsd3_proc_##name,		\
+   (kxdrproc_t) nfs3svc_decode_##argt##args,	\
+   (kxdrproc_t) nfs3svc_encode_##rest##res,	\
+   (kxdrproc_t) nfs3svc_release_##relt,		\
+   sizeof(struct nfsd3_##argt##args),		\
+   sizeof(struct nfsd3_##rest##res),		\
+   0,						\
+   cache					\
+ }
+struct svc_procedure		nfsd_procedures3[22] = {
+  PROC(null,	 void,		void,		void,	 RC_NOCACHE),
+  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
+  PROC(setattr,  sattr,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(lookup,	 dirop,		dirop,		fhandle2, RC_NOCACHE),
+  PROC(access,	 access,	access,		fhandle,  RC_NOCACHE),
+  PROC(readlink, fhandle,	readlink,	fhandle,  RC_NOCACHE),
+  PROC(read,	 read,		read,		fhandle, RC_NOCACHE),
+  PROC(write,	 write,		write,		fhandle,  RC_REPLBUFF),
+  PROC(create,	 create,	create,		fhandle2, RC_REPLBUFF),
+  PROC(mkdir,	 mkdir,		create,		fhandle2, RC_REPLBUFF),
+  PROC(symlink,	 symlink,	create,		fhandle2, RC_REPLBUFF),
+  PROC(mknod,	 mknod,		create,		fhandle2, RC_REPLBUFF),
+  PROC(remove,	 dirop,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(rmdir,	 dirop,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(rename,	 rename,	rename,		fhandle2, RC_REPLBUFF),
+  PROC(link,	 link,		link,		fhandle2, RC_REPLBUFF),
+  PROC(readdir,	 readdir,	readdir,	fhandle,  RC_NOCACHE),
+  PROC(readdirplus,readdirplus,	readdir,	fhandle,  RC_NOCACHE),
+  PROC(fsstat,	 fhandle,	fsstat,		void,     RC_NOCACHE),
+  PROC(fsinfo,   fhandle,	fsinfo,		void,     RC_NOCACHE),
+  PROC(pathconf, fhandle,	pathconf,	void,     RC_NOCACHE),
+  PROC(commit,	 commit,	commit,		fhandle,  RC_REPLBUFF)
+};
diff -urN linux-2.2.18-pre15/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c
--- linux-2.2.18-pre15/fs/nfsd/nfs3xdr.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfs3xdr.c	Mon Dec  3 20:34:12 2001
@@ -3,7 +3,7 @@
  *
  * XDR support for nfsd/protocol version 3.
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/types.h>
@@ -17,16 +17,16 @@
 
 #define NFSDDBG_FACILITY		NFSDDBG_XDR
 
-u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
-	nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
-	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
-	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
-
 #ifdef NFSD_OPTIMIZE_SPACE
 # define inline
 #endif
 
 /*
+ * Size of encoded NFS3 file handle, in words
+ */
+#define NFS3_FHANDLE_WORDS	(1 + XDR_QUADLEN(sizeof(struct knfs_fh)))
+
+/*
  * Mapping of S_IF* types to NFS file types
  */
 static u32	nfs3_ftypes[] = {
@@ -37,48 +37,9 @@
 };
 
 /*
- * Initialization of NFS status variables
- */
-void
-nfs3xdr_init(void)
-{
-	static int	inited = 0;
-
-	if (inited)
-		return;
-
-	nfs_ok = htonl(NFS_OK);
-	nfserr_perm = htonl(NFSERR_PERM);
-	nfserr_noent = htonl(NFSERR_NOENT);
-	nfserr_io = htonl(NFSERR_IO);
-	nfserr_nxio = htonl(NFSERR_NXIO);
-	nfserr_acces = htonl(NFSERR_ACCES);
-	nfserr_exist = htonl(NFSERR_EXIST);
-	nfserr_nodev = htonl(NFSERR_NODEV);
-	nfserr_notdir = htonl(NFSERR_NOTDIR);
-	nfserr_isdir = htonl(NFSERR_ISDIR);
-	nfserr_fbig = htonl(NFSERR_FBIG);
-	nfserr_nospc = htonl(NFSERR_NOSPC);
-	nfserr_rofs = htonl(NFSERR_ROFS);
-	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
-	nfserr_dquot = htonl(NFSERR_DQUOT);
-	nfserr_stale = htonl(NFSERR_STALE);
-
-	inited = 1;
-}
-
-/*
  * XDR functions for basic NFS types
  */
 static inline u32 *
-enc64(u32 *p, u64 val)
-{
-	*p++ = (val >> 32);
-	*p++ = (val & 0xffffffff);
-	return p;
-}
-
-static inline u32 *
 dec64(u32 *p, u64 *valp)
 {
 	*valp  = ((u64) ntohl(*p++)) << 32;
@@ -103,13 +64,10 @@
 static inline u32 *
 decode_fh(u32 *p, struct svc_fh *fhp)
 {
-	if (*p++ != sizeof(struct knfs_fh))
+	if (ntohl(*p++) != sizeof(struct knfs_fh))
 		return NULL;
 
 	memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh));
-	fhp->fh_inode  = NULL;
-	fhp->fh_export = NULL;
-
 	return p + (sizeof(struct knfs_fh) >> 2);
 }
 
@@ -179,27 +137,35 @@
 		iap->ia_gid = ntohl(*p++);
 	}
 	if (*p++) {
+		u64	newsize;
+
 		iap->ia_valid |= ATTR_SIZE;
-		iap->ia_size = ntohl(*p++);
+		p = dec64(p, &newsize);
+		if (newsize <= NFS_OFFSET_MAX)
+			iap->ia_size = (u32) newsize;
+		else
+			iap->ia_size = ~(size_t) 0;
 	}
-	if ((tmp = *p++) == 1) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_ATIME;
-	} else if (tmp == 2) {
+	} else if (tmp == 2) {		/* set to client time */
 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
 		iap->ia_atime = ntohl(*p++), p++;
 	}
-	if ((tmp = *p++) != 0) {
-		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
-	} else if (tmp == 2) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_MTIME;
+	} else if (tmp == 2) {		/* set to client time */
+		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
 		iap->ia_mtime = ntohl(*p++), p++;
 	}
 	return p;
 }
 
 static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
 {
+	struct inode	*inode = dentry->d_inode;
+
 	if (!inode) {
 		printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__);
 		return NULL;
@@ -215,7 +181,16 @@
 	} else {
 		p = enc64(p, (u64) inode->i_size);
 	}
-	p = enc64(p, inode->i_blksize * inode->i_blocks);
+	/*
+	 *  For the 'used' member, we take i_blocks if set; assuming 512-byte
+	 *  units.  Some FSs don't set this, so all we can do then is
+	 *  use the size.
+	 */
+	if (inode->i_blocks) {
+		p = enc64(p,  ((u64)inode->i_blocks)<<9 );
+	} else {
+		p = enc64(p, (u64) inode->i_size);
+	}
 	*p++ = htonl((u32) MAJOR(inode->i_rdev));
 	*p++ = htonl((u32) MINOR(inode->i_rdev));
 	p = enc64(p, (u64) inode->i_dev);
@@ -227,19 +202,54 @@
 	return p;
 }
 
+static inline u32 *
+encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+	struct inode	*inode = fhp->fh_dentry->d_inode;
+
+	/* Attributes to follow */
+	*p++ = xdr_one;
+
+	*p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
+	*p++ = htonl((u32) fhp->fh_post_mode);
+	*p++ = htonl((u32) fhp->fh_post_nlink);
+	*p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
+	*p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
+	if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
+		p = enc64(p, (u64) NFS3_MAXPATHLEN);
+	} else {
+		p = enc64(p, (u64) fhp->fh_post_size);
+	}
+	if (fhp->fh_post_blocks) {
+		p = enc64(p, ((u64)fhp->fh_post_blocks)<<9);
+	} else {
+		p = enc64(p, (u64) fhp->fh_post_size);
+	}
+	*p++ = htonl((u32) MAJOR(fhp->fh_post_rdev));
+	*p++ = htonl((u32) MINOR(fhp->fh_post_rdev));
+	p = enc64(p, (u64) inode->i_dev);
+	p = enc64(p, (u64) inode->i_ino);
+	p = encode_time3(p, fhp->fh_post_atime);
+	p = encode_time3(p, fhp->fh_post_mtime);
+	p = encode_time3(p, fhp->fh_post_ctime);
+
+	return p;
+}
+
 /*
  * Encode post-operation attributes.
  * The inode may be NULL if the call failed because of a stale file
  * handle. In this case, no attributes are returned.
  */
 static u32 *
-encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
 {
-	if (inode == NULL) {
-		*p++ = xdr_zero;
-		return p;
+	if (dentry && dentry->d_inode != NULL) {
+		*p++ = xdr_one;		/* attributes follow */
+		return encode_fattr3(rqstp, p, dentry);
 	}
-	return encode_fattr3(rqstp, p, inode);
+	*p++ = xdr_zero;
+	return p;
 }
 
 /*
@@ -248,17 +258,22 @@
 static u32 *
 encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-	struct inode	*inode = fhp->fh_inode;
+	struct dentry	*dentry = fhp->fh_dentry;
 
-	if (fhp->fh_post_version == inode->i_version) {
-		*p++ = xdr_one;
-		p = enc64(p, (u64) fhp->fh_pre_size);
-		p = encode_time3(p, fhp->fh_pre_mtime);
-		p = encode_time3(p, fhp->fh_pre_ctime);
-	} else {
-		*p++ = xdr_zero;
+	if (dentry && dentry->d_inode && fhp->fh_post_saved) {
+		if (fhp->fh_pre_saved) {
+			*p++ = xdr_one;
+			p = enc64(p, (u64) fhp->fh_pre_size);
+			p = encode_time3(p, fhp->fh_pre_mtime);
+			p = encode_time3(p, fhp->fh_pre_ctime);
+		} else {
+			*p++ = xdr_zero;
+		}
+		return encode_saved_post_attr(rqstp, p, fhp);
 	}
-	return encode_post_op_attr(rqstp, p, inode);
+	/* no pre- or post-attrs */
+	*p++ = xdr_zero;
+	return encode_post_op_attr(rqstp, p, dentry);
 }
 
 /*
@@ -299,10 +314,12 @@
 					struct nfsd3_sattrargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = decode_sattr3(p, &args->attrs))
-	 || (*p++ && !(p = decode_time3(p, &args->guardtime))))
+	 || !(p = decode_sattr3(p, &args->attrs)))
 		return 0;
 
+	if ((args->check_guard = ntohl(*p++)) != 0)
+		p = decode_time3(p, &args->guardtime);
+
 	return xdr_argsize_check(rqstp, p);
 }
 
@@ -333,10 +350,10 @@
 					struct nfsd3_readargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset))
-	 || !(p = dec64(p, &args->count)))
+	 || !(p = dec64(p, &args->offset)))
 		return 0;
 
+	args->count = ntohl(*p++);
 	return xdr_argsize_check(rqstp, p);
 }
 
@@ -345,14 +362,14 @@
 					struct nfsd3_writeargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset))
-	 || !(p = dec64(p, &args->count)))
+	 || !(p = dec64(p, &args->offset)))
 		return 0;
 
+	args->count = ntohl(*p++);
 	args->stable = ntohl(*p++);
 	args->len = ntohl(*p++);
 	args->data = (char *) p;
-	p += (args->len + 3) >> 2;
+	p += XDR_QUADLEN(args->len);
 
 	return xdr_argsize_check(rqstp, p);
 }
@@ -366,11 +383,12 @@
 		return 0;
 
 	switch (args->createmode = ntohl(*p++)) {
-	case 0: case 1:
+	case NFS3_CREATE_UNCHECKED:
+	case NFS3_CREATE_GUARDED:
 		if (!(p = decode_sattr3(p, &args->attrs)))
 			return 0;
 		break;
-	case 2:
+	case NFS3_CREATE_EXCLUSIVE:
 		args->verf = p;
 		p += 2;
 		break;
@@ -460,8 +478,9 @@
 {
 	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
-	args->cookie = ntohl(*p++);
+	p = dec64(p, &args->cookie);
 	args->verf   = p; p += 2;
+	args->dircount = ~0;
 	args->count  = ntohl(*p++);
 
 	return xdr_argsize_check(rqstp, p);
@@ -473,7 +492,7 @@
 {
 	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
-	args->cookie   = ntohl(*p++);
+	p = dec64(p, &args->cookie);
 	args->verf     = p; p += 2;
 	args->dircount = ntohl(*p++);
 	args->count    = ntohl(*p++);
@@ -485,9 +504,9 @@
 nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_commitargs *args)
 {
-	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset)))
+	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
+	p = dec64(p, &args->offset);
 	args->count = ntohl(*p++);
 
 	return xdr_argsize_check(rqstp, p);
@@ -496,12 +515,23 @@
 /*
  * XDR encode functions
  */
+/*
+ * There must be an encoding function for void results so svc_process
+ * will work properly.
+ */
+int
+nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
 /* GETATTR */
 int
 nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_attrstat *resp)
 {
-	if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
+	if (resp->status == 0
+	 && !(p = encode_fattr3(rqstp, p, resp->fh.fh_dentry)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -518,15 +548,14 @@
 
 /* LOOKUP */
 int
-nfs3svc_encode_lookupres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_lookupres *resp)
+nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_diropres *resp)
 {
 	if (resp->status == 0) {
 		p = encode_fh(p, &resp->fh);
-		if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
-			return 0;
+		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	}
-	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_dentry);
 	return xdr_ressize_check(rqstp, p);
 }
 
@@ -535,7 +564,7 @@
 nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_accessres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0)
 		*p++ = htonl(resp->access);
 	return xdr_ressize_check(rqstp, p);
@@ -546,7 +575,7 @@
 nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readlinkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->len);
 		p += XDR_QUADLEN(resp->len);
@@ -559,7 +588,7 @@
 nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->count);
 		*p++ = htonl(resp->eof);
@@ -587,11 +616,12 @@
 /* CREATE, MKDIR, SYMLINK, MKNOD */
 int
 nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_createres *resp)
+					struct nfsd3_diropres *resp)
 {
 	if (resp->status == 0) {
+		*p++ = xdr_one;
 		p = encode_fh(p, &resp->fh);
-		p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	}
 	p = encode_wcc_data(rqstp, p, &resp->dirfh);
 	return xdr_ressize_check(rqstp, p);
@@ -612,7 +642,7 @@
 nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_linkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	p = encode_wcc_data(rqstp, p, &resp->tfh);
 	return xdr_ressize_check(rqstp, p);
 }
@@ -622,73 +652,116 @@
 nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readdirres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		/* stupid readdir cookie */
-		*p++ = ntohl(resp->fh.fh_inode->i_mtime);
-		*p++ = xdr_zero;
-		p = resp->list_end;
+		memcpy(p, resp->verf, 8); p += 2;
+		p += XDR_QUADLEN(resp->count);
 	}
 
 	return xdr_ressize_check(rqstp, p);
 }
 
-#define NFS3_ENTRYPLUS_BAGGAGE	((1 + 20 + 1 + NFS3_FHSIZE) << 2)
-int
-nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
-				int namlen, unsigned long offset, ino_t ino)
+/*
+ * Encode a directory entry. This one works for both normal readdir
+ * and readdirplus.
+ * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
+ * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
+ * 
+ * The readdirplus baggage is 1+21 words for post_op_attr, plus the
+ * file handle.
+ */
+
+#define NFS3_ENTRY_BAGGAGE	(2 + 1 + 2 + 1)
+#define NFS3_ENTRYPLUS_BAGGAGE	(1 + 21 + 1 + (NFS3_FHSIZE >> 2))
+static int
+encode_entry(struct readdir_cd *cd, const char *name,
+			int namlen, off_t offset, ino_t ino, int plus)
 {
 	u32		*p = cd->buffer;
 	int		buflen, slen, elen;
-	struct svc_fh	fh;
 
-	if (offset > ~((u64) 0))
-		return -EINVAL;
 	if (cd->offset)
-		*cd->offset = htonl(offset);
+		enc64(cd->offset, (u64) offset);
 
-	/* For readdirplus, look up the inode */
-	if (cd->plus && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh))
+	/* nfsd_readdir calls us with name == 0 when it wants us to
+	 * set the last offset entry. */
+	if (name == 0)
 		return 0;
 
+	/*
+	dprintk("encode_entry(%.*s @%ld%s)\n",
+		namlen, name, (long) offset, plus? " plus" : "");
+	 */
+
 	/* truncate filename if too long */
 	if (namlen > NFS3_MAXNAMLEN)
 		namlen = NFS3_MAXNAMLEN;
 
 	slen = XDR_QUADLEN(namlen);
-	elen = slen + (cd->plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
-	if ((buflen = cd->buflen - elen - 4) < 0) {
+	elen = slen + NFS3_ENTRY_BAGGAGE
+		+ (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
+	if ((buflen = cd->buflen - elen) < 0) {
 		cd->eob = 1;
-		if (cd->plus)
-			fh_put(&fh);
 		return -EINVAL;
 	}
-	*p++ = xdr_one;			/* mark entry present */
-	*p++ = xdr_zero;		/* file id (64 bit) */
-	*p++ = htonl((u32) ino);
-	*p++ = htonl((u32) namlen);	/* name length & name */
+	*p++ = xdr_one;				   /* mark entry present */
+	p    = enc64(p, ino);			   /* file id */
+#ifdef XDR_ENCODE_STRING_TAKES_LENGTH
+	p    = xdr_encode_string(p, name, namlen); /* name length & name */
+#else
+	/* just like nfsproc.c */
+	*p++ = htonl((u32) namlen);
+	p[slen - 1] = 0;		/* don't leak kernel data */
 	memcpy(p, name, namlen);
 	p += slen;
+#endif
+
+	cd->offset = p;			/* remember pointer */
+	p = enc64(p, NFS_OFFSET_MAX);	/* offset of next entry */
 
 	/* throw in readdirplus baggage */
-	if (cd->plus) {
-		p = encode_post_op_attr(cd->rqstp, p, fh.fh_inode);
-		p = encode_fh(p, &fh);
-		fh_put(&fh);
-	}
+	if (plus) {
+		struct svc_fh	fh;
 
-	cd->offset = p;			/* remember pointer */
-	p = enc64(p, ~(u64) 0);	/* offset of next entry */
+		fh_init(&fh);
+		/* Disabled for now because of lock-up */
+		if (0 && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh) == 0) {
+			p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+			p = encode_fh(p, &fh);
+			fh_put(&fh);
+		} else {
+			/* Didn't find this entry... weird.
+			 * Proceed without the attrs anf fh anyway.
+			 */
+			*p++ = 0;
+			*p++ = 0;
+		}
+	}
 
 	cd->buflen = buflen;
 	cd->buffer = p;
 	return 0;
 }
 
+int
+nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
+				int namlen, off_t offset, ino_t ino)
+{
+	return encode_entry(cd, name, namlen, offset, ino, 0);
+}
+
+int
+nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
+				int namlen, off_t offset, ino_t ino)
+{
+	return encode_entry(cd, name, namlen, offset, ino, 1);
+}
+
 /* FSSTAT */
 int
-nfs3svc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_statfsres *resp)
+nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_fsstatres *resp)
 {
 	struct statfs	*s = &resp->stats;
 	u64		bs = s->f_bsize;
@@ -722,9 +795,9 @@
 		*p++ = htonl(resp->f_wtpref);
 		*p++ = htonl(resp->f_wtmult);
 		*p++ = htonl(resp->f_dtpref);
-		*p++ = htonl(resp->f_maxfilesize);
+		p = enc64(p, resp->f_maxfilesize);
+		*p++ = xdr_one;
 		*p++ = xdr_zero;
-		*p++ = htonl(1000000000 / HZ);
 		*p++ = htonl(resp->f_properties);
 	}
 
@@ -741,8 +814,8 @@
 	if (resp->status == 0) {
 		*p++ = htonl(resp->p_link_max);
 		*p++ = htonl(resp->p_name_max);
-		*p++ = xdr_one;	/* always reject long file names */
-		*p++ = xdr_one;	/* chown restricted */
+		*p++ = htonl(resp->p_no_trunc);
+		*p++ = htonl(resp->p_chown_restricted);
 		*p++ = htonl(resp->p_case_insensitive);
 		*p++ = htonl(resp->p_case_preserving);
 	}
@@ -769,7 +842,7 @@
  */
 int
 nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd_fhandle *resp)
+					struct nfsd3_attrstat *resp)
 {
 	fh_put(&resp->fh);
 	return 1;
@@ -777,7 +850,7 @@
 
 int
 nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_fhandle2 *resp)
+					struct nfsd3_fhandle_pair *resp)
 {
 	fh_put(&resp->fh1);
 	fh_put(&resp->fh2);
diff -urN linux-2.2.18-pre15/fs/nfsd/nfscache.c linux/fs/nfsd/nfscache.c
--- linux-2.2.18-pre15/fs/nfsd/nfscache.c	Sun Jan 24 21:54:35 1999
+++ linux/fs/nfsd/nfscache.c	Mon Dec  3 20:34:12 2001
@@ -143,8 +143,9 @@
 nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
 {
 	struct svc_cacherep	*rh, *rp;
-	struct svc_client	*clp = rqstp->rq_client;
 	u32			xid = rqstp->rq_xid,
+				proto = rqstp->rq_prot,
+				vers = rqstp->rq_vers,
 				proc = rqstp->rq_proc;
 	unsigned long		age;
 
@@ -158,7 +159,9 @@
 	while ((rp = rp->c_hash_next) != rh) {
 		if (rp->c_state != RC_UNUSED &&
 		    xid == rp->c_xid && proc == rp->c_proc &&
-		    exp_checkaddr(clp, rp->c_client)) {
+		    proto == rp->c_prot && vers == rp->c_vers &&
+		    time_before(jiffies, rp->c_timestamp + 120*HZ) &&
+		    memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, rqstp->rq_addrlen)==0) {
 			nfsdstats.rchits++;
 			goto found_entry;
 		}
@@ -195,7 +198,11 @@
 	rp->c_state = RC_INPROG;
 	rp->c_xid = xid;
 	rp->c_proc = proc;
-	rp->c_client = rqstp->rq_addr.sin_addr;
+	memcpy(&rp->c_addr, &rqstp->rq_addr, sizeof(rp->c_addr));
+	rp->c_prot = proto;
+	rp->c_vers = vers;
+	rp->c_timestamp = jiffies;
+
 	hash_refile(rp);
 
 	/* release any buffer */
@@ -210,12 +217,13 @@
 found_entry:
 	/* We found a matching entry which is either in progress or done. */
 	age = jiffies - rp->c_timestamp;
-	rp->c_timestamp = jiffies;
-	lru_put_front(rp);
 
 	/* Request being processed or excessive rexmits */
 	if (rp->c_state == RC_INPROG || age < RC_DELAY)
 		return RC_DROPIT;
+
+	rp->c_timestamp = jiffies;
+	lru_put_front(rp);
 
 	/* From the hall of fame of impractical attacks:
 	 * Is this a user who tries to snoop on the cache? */
diff -urN linux-2.2.18-pre15/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- linux-2.2.18-pre15/fs/nfsd/nfsctl.c	Wed May  3 17:16:46 2000
+++ linux/fs/nfsd/nfsctl.c	Mon Dec  3 20:34:12 2001
@@ -18,7 +18,6 @@
 #include <linux/fcntl.h>
 #include <linux/net.h>
 #include <linux/in.h>
-#include <linux/version.h>
 #include <linux/unistd.h>
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
@@ -363,7 +362,6 @@
 	do_nfsservctl = NULL;
 	nfsd_export_shutdown();
 	nfsd_cache_shutdown();
-	nfsd_fh_free();
 	remove_proc_entry("fs/nfs/time-diff-margin", NULL);
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
diff -urN linux-2.2.18-pre15/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- linux-2.2.18-pre15/fs/nfsd/nfsfh.c	Wed May  3 17:16:46 2000
+++ linux/fs/nfsd/nfsfh.c	Mon Dec  3 20:34:12 2001
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
+ * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
  */
 
 #include <linux/sched.h>
@@ -22,334 +23,50 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FH
 #define NFSD_PARANOIA 1
 /* #define NFSD_DEBUG_VERBOSE 1 */
-/* #define NFSD_DEBUG_VERY_VERBOSE 1 */
 
-extern unsigned long max_mapnr;
-
-#define NFSD_FILE_CACHE 0
-#define NFSD_DIR_CACHE  1
-struct fh_entry {
-	struct dentry * dentry;
-	unsigned long reftime;
-	ino_t	ino;
-	kdev_t	dev;
-};
-
-#define NFSD_MAXFH \
-  (((nfsd_nservers + 1) >> 1) * PAGE_SIZE/sizeof(struct fh_entry))
-static struct fh_entry *filetable = NULL;
-static struct fh_entry *dirstable = NULL;
 
 static int nfsd_nr_verified = 0;
 static int nfsd_nr_put = 0;
-static unsigned long nfsd_next_expire = 0;
-
-static int add_to_fhcache(struct dentry *, int);
-struct dentry * lookup_inode(kdev_t, ino_t, ino_t);
-
-static LIST_HEAD(fixup_head);
-static LIST_HEAD(path_inuse);
-static int nfsd_nr_fixups = 0;
-static int nfsd_nr_paths = 0;
-#define NFSD_MAX_PATHS 500
-#define NFSD_MAX_FIXUPS 500
-#define NFSD_MAX_FIXUP_AGE 30*HZ
-
-struct nfsd_fixup {
-	struct list_head lru;
-	unsigned long reftime;
-	ino_t	dirino;
-	ino_t	ino;
-	kdev_t	dev;
-	ino_t	new_dirino;
-};
-
-struct nfsd_path {
-	struct list_head lru;
-	unsigned long reftime;
-	int	users;
-	ino_t	ino;
-	kdev_t	dev;
-	char	name[1];
-};
-
-static struct nfsd_fixup *
-find_cached_lookup(kdev_t dev, ino_t dirino, ino_t ino)
-{
-	struct list_head *tmp = fixup_head.next;
-
-	for (; tmp != &fixup_head; tmp = tmp->next) {
-		struct nfsd_fixup *fp;
-
-		fp = list_entry(tmp, struct nfsd_fixup, lru);
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("fixup %lu %lu, %lu %lu %s %s\n",
-        fp->ino, ino,
-	fp->dirino, dirino,
-	kdevname(fp->dev), kdevname(dev));
-#endif
-		if (fp->ino != ino)
-			continue;
-		if (fp->dirino != dirino)
-			continue;
-		if (fp->dev != dev)
-			continue;
-		fp->reftime = jiffies;	
-		list_del(tmp);
-		list_add(tmp, &fixup_head);
-		return fp;
-	}
-	return NULL;
-}
-
-/*
- * Save the dirino from a rename.
- */
-void
-add_to_rename_cache(ino_t new_dirino,
-                    kdev_t dev, ino_t dirino, ino_t ino)
-{
-	struct nfsd_fixup *fp;
-
-	if (dirino == new_dirino)
-		return;
-
-	fp = find_cached_lookup(dev, 
-				dirino,
-				ino);
-	if (fp) {
-		fp->new_dirino = new_dirino;
-		return;
-	}
-
-	/*
-	 * Add a new entry. The small race here is unimportant:
-	 * if another task adds the same lookup, both entries
-	 * will be consistent.
-	 */
-	fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL);
-	if (fp) {
-		fp->dirino = dirino;
-		fp->ino = ino;
-		fp->dev = dev;
-		fp->new_dirino = new_dirino;
-		list_add(&fp->lru, &fixup_head);
-		nfsd_nr_fixups++;
-	}
-}
-
-/*
- * Save the dentry pointer from a successful lookup.
- */
-
-static void free_fixup_entry(struct nfsd_fixup *fp)
-{
-	list_del(&fp->lru);
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("free_rename_entry: %lu->%lu %lu/%s\n",
-		fp->dirino,
-		fp->new_dirino,
-		fp->ino,
-		kdevname(fp->dev),
-		(jiffies - fp->reftime));
-#endif
-	kfree(fp);
-	nfsd_nr_fixups--;
-}
-
-/*
- * Copy a dentry's path into the specified buffer.
- */
-static int copy_path(char *buffer, struct dentry *dentry, int namelen)
-{
-	char *p, *b = buffer;
-	int result = 0, totlen = 0, len; 
-
-	while (1) {
-		struct dentry *parent;
-		dentry = dentry->d_covers;
-		parent = dentry->d_parent;
-		len = dentry->d_name.len;
-		p = (char *) dentry->d_name.name + len;
-		totlen += len;
-		if (totlen > namelen)
-			goto out;
-		while (len--)
-			*b++ = *(--p);
-		if (dentry == parent)
-			break;
-		dentry = parent;
-		totlen++;
-		if (totlen > namelen)
-			goto out;
-		*b++ = '/';
-	}
-	*b = 0;
-
-	/*
-	 * Now reverse in place ...
-	 */
-	p = buffer;
-	while (p < b) {
-		char c = *(--b);
-		*b = *p;
-		*p++ = c;
-	} 
-	result = 1;
-out:
-	return result;
-}
-
-/*
- * Add a dentry's path to the path cache.
- */
-static int add_to_path_cache(struct dentry *dentry)
-{
-	struct inode *inode = dentry->d_inode;
-	struct dentry *this;
-	struct nfsd_path *new;
-	int len, result = 0;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: caching %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-	/*
-	 * Get the length of the full pathname.
-	 */
-restart:
-	len = 0;
-	this = dentry;
-	while (1) {
-		struct dentry *parent;
-		this = this->d_covers;
-		parent = this->d_parent;
-		len += this->d_name.len;
-		if (this == parent)
-			break;
-		this = parent;
-		len++;
-	}
-	/*
-	 * Allocate a structure to hold the path.
-	 */
-	new = kmalloc(sizeof(struct nfsd_path) + len, GFP_KERNEL);
-	if (new) {
-		new->users = 0;	
-		new->reftime = jiffies;	
-		new->ino = inode->i_ino;
-		new->dev = inode->i_dev;
-		result = copy_path(new->name, dentry, len);
-		if (!result)
-			goto retry;
-		list_add(&new->lru, &path_inuse);
-		nfsd_nr_paths++;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: added %s, paths=%d\n", new->name, nfsd_nr_paths);
-#endif
-	}
-	return result;
-
-	/*
-	 * If the dentry's path length changed, just try again.
-	 */
-retry:
-	kfree(new);
-	printk(KERN_DEBUG "add_to_path_cache: path length changed, retrying\n");
-	goto restart;
-}
-
-/*
- * Search for a path entry for the specified (dev, inode).
- */
-static struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino)
-{
-	struct nfsd_path *pe;
-	struct list_head *tmp;
 
-	for (tmp = path_inuse.next; tmp != &path_inuse; tmp = tmp->next) {
-		pe = list_entry(tmp, struct nfsd_path, lru);
-		if (pe->ino != ino)
-			continue;
-		if (pe->dev != dev)
-			continue;
-		list_del(tmp);
-		list_add(tmp, &path_inuse);
-		pe->users++;
-		pe->reftime = jiffies;
-#ifdef NFSD_PARANOIA
-printk("get_path_entry: found %s for %s/%ld\n", pe->name, kdevname(dev), ino);
-#endif
-		return pe;
-	}
-	return NULL;
-}
-
-static void put_path(struct nfsd_path *pe)
-{
-	pe->users--;
-}
-
-static void free_path_entry(struct nfsd_path *pe)
-{
-	if (pe->users)
-		printk(KERN_DEBUG "free_path_entry: %s in use, users=%d\n",
-			pe->name, pe->users);
-	list_del(&pe->lru);
-	kfree(pe);
-	nfsd_nr_paths--;
-}
 
 struct nfsd_getdents_callback {
-	struct nfsd_dirent *dirent;
-	ino_t dirino;		/* parent inode number */
-	int found;		/* dirent inode matched? */
+	struct qstr *name;	/* name that was found. name->name already points to a buffer */
+	unsigned long ino;	/* the inum we are looking for */
+	int found;		/* inode matched? */
 	int sequence;		/* sequence counter */
 };
 
-struct nfsd_dirent {
-	ino_t ino;		/* preset to desired entry */
-	int len;
-	char name[256];
-};
-
 /*
- * A rather strange filldir function to capture the inode number
- * for the second entry (the parent inode) and the name matching
- * the specified inode number.
+ * A rather strange filldir function to capture
+ * the name matching the specified inode number.
  */
-static int filldir_one(void * __buf, const char * name, int len, 
+static int filldir_one(void * __buf, const char * name, int len,
 			off_t pos, ino_t ino)
 {
 	struct nfsd_getdents_callback *buf = __buf;
-	struct nfsd_dirent *dirent = buf->dirent;
+	struct qstr *qs = buf->name;
+	char *nbuf = (char*)qs->name; /* cast is to get rid of "const" */
 	int result = 0;
 
 	buf->sequence++;
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("filldir_one: seq=%d, ino=%lu, name=%s\n", buf->sequence, ino, name);
+#ifdef NFSD_DEBUG_VERBOSE
+dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
 #endif
-	if (buf->sequence == 2) {
-		buf->dirino = ino;
-		goto out;
-	}
-	if (dirent->ino == ino) {
-		dirent->len = len;
-		memcpy(dirent->name, name, len);
-		dirent->name[len] = '\0';
+	if (buf->ino == ino) {
+		qs->len = len;
+		memcpy(nbuf, name, len);
+		nbuf[len] = '\0';
 		buf->found = 1;
 		result = -1;
 	}
-out:
 	return result;
 }
 
 /*
- * Read a directory and return the parent inode number and the name
- * of the specified entry. The dirent must be initialized with the
- * inode number of the desired entry.
+ * Read a directory and return the name of the specified entry.  
+ * i_sem is already down().
  */
-static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
+static int get_ino_name(struct dentry *dentry, struct qstr *name, unsigned long ino)
 {
 	struct inode *dir = dentry->d_inode;
 	int error;
@@ -372,15 +89,13 @@
 	if (!file.f_op->readdir)
 		goto out_close;
 
-	buffer.dirent = dirent;
-	buffer.dirino = 0;
+	buffer.name = name;
+	buffer.ino = ino;
 	buffer.found = 0;
 	buffer.sequence = 0;
 	while (1) {
 		int old_seq = buffer.sequence;
-		down(&dir->i_sem);
 		error = file.f_op->readdir(&file, &buffer, filldir_one);
-		up(&dir->i_sem);
 		if (error < 0)
 			break;
 
@@ -391,7 +106,6 @@
 		if (old_seq == buffer.sequence)
 			break;
 	}
-	dirent->ino = buffer.dirino;
 
 out_close:
 	if (file.f_op->release)
@@ -400,716 +114,380 @@
 	return error;
 }
 
-/*
- * Look up a dentry given inode and parent inode numbers.
- *
- * This relies on the ability of a Unix-like filesystem to return
- * the parent inode of a directory as the ".." (second) entry.
- *
- * This could be further optimized if we had an efficient way of
- * searching for a dentry given the inode: as we walk up the tree,
- * it's likely that a dentry exists before we reach the root.
+/* this should be provided by each filesystem in an nfsd_operations interface as
+ * iget isn't really the right interface
  */
-struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino)
+static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
 {
-	struct super_block *sb;
-	struct dentry *root, *dentry, *result;
-	struct inode *dir;
-	char *name;
-	unsigned long page;
-	ino_t root_ino;
-	int error;
-	struct nfsd_dirent dirent;
 
-	result = ERR_PTR(-ENOMEM);
-	page = __get_free_page(GFP_KERNEL);
-	if (!page)
-		goto out;
-
-	/*
-	 * Get the root dentry for the device.
-	 */
-	result = ERR_PTR(-ENOENT);
-	sb = get_super(dev);
-	if (!sb)
-		goto out_page;
-	root = dget(sb->s_root);
-	root_ino = root->d_inode->i_ino; /* usually 2 */
-
-	name = (char *) page + PAGE_SIZE;
-	*(--name) = 0;
-
-	/*
-	 * Walk up the tree to construct the name string.
-	 * When we reach the root inode, look up the name
-	 * relative to the root dentry.
+	/* 
+	 * ext2fs' read_inode has been strengthed to return a bad_inode if 
+	 * the inode had been deleted.
+	 *
+	 * Currently we don't know the generation for parent directory, 
+	 * so a generation of 0 means "accept any"
 	 */
-	while (1) {
-		if (ino == root_ino) {
-			if (*name == '/')
-				name++;
-			/*
-			 * Note: this dput()s the root dentry.
-			 */
-			result = lookup_dentry(name, root, 0);
-			break;
-		}
-
-		/*
-		 *  Fix for /// bad export bug: if dirino is the root,
-		 *  get the real root dentry rather than creating a temporary
-		 *  "root" dentry.  XXX We could extend this to use
-		 *  any existing dentry for the located 'dir', but all
-		 *  of this code is going to be completely rewritten soon,
-		 *  so I won't bother. 
-		 */
-
-		if (dirino == root_ino) {
-			dentry = dget(root);
-		}
-		else {
-			result = ERR_PTR(-ENOENT);
-			dir = iget_in_use(sb, dirino);
-			if (!dir)
-				goto out_root;
-			dentry = d_alloc_root(dir, NULL);
-			if (!dentry)
-				goto out_iput;
-		}
-
-		/*
-		 * Get the name for this inode and the next parent inode.
-		 */
-		dirent.ino = ino;
-		error = get_parent_ino(dentry, &dirent);
-		result = ERR_PTR(error);
-		dput(dentry);
-		if (error)
-			goto out_root;
-		/*
-		 * Prepend the name to the buffer.
-		 */
-		result = ERR_PTR(-ENAMETOOLONG);
-		name -= (dirent.len + 1);
-		if ((unsigned long) name <= page)
-			goto out_root;
-		memcpy(name + 1, dirent.name, dirent.len);
-		*name = '/';
-
-		/*
-		 * Make sure we can't get caught in a loop ...
-		 */
-		if (dirino == dirent.ino && dirino != root_ino) {
-			printk(KERN_DEBUG 
-			       "lookup_inode: looping?? (ino=%ld, path=%s)\n",
-				dirino, name);	
-			goto out_root;
-		}
-		ino = dirino;
-		dirino = dirent.ino;
-	}
-
-out_page:
-	free_page(page);
-out:
-	return result;
-
-	/*
-	 * Error exits ...
-	 */
-out_iput:
-	result = ERR_PTR(-ENOMEM);
-	iput(dir);
-out_root:
-	dput(root);
-	goto out_page;
-}
-
-/*
- * Find an entry in the cache matching the given dentry pointer.
- */
-static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
-				struct fh_entry **empty)
-{
-	struct fh_entry *fhe;
-	int i, found = (empty == NULL) ? 1 : 0;
-
-	if (!dentry)
-		goto out;
-
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (fhe->dentry == dentry) {
-			fhe->reftime = jiffies;
-			return fhe;
-		}
-		if (!found && !fhe->dentry) {
-			found = 1;
-			*empty = fhe;
-		}
-	}
-out:
-	return NULL;
-}
-
-/*
- * Expire a cache entry.
- */
-static void expire_fhe(struct fh_entry *empty, int cache)
-{
-	struct dentry *dentry = empty->dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%lu\n",
-(cache == NFSD_FILE_CACHE) ? "file" : "dir",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
-#endif
-	empty->dentry = NULL;	/* no dentry */
-	/*
-	 * Add the parent to the dir cache before releasing the dentry,
-	 * and check whether to save a copy of the dentry's path.
-	 */
-	if (dentry != dentry->d_parent) {
-		struct dentry *parent = dget(dentry->d_parent);
-		if (add_to_fhcache(parent, NFSD_DIR_CACHE))
-			nfsd_nr_verified++;
-		else
-			dput(parent);
-		/*
-		 * If we're expiring a directory, copy its path.
-		 */
-		if (cache == NFSD_DIR_CACHE) {
-			add_to_path_cache(dentry);
-		}
-	}
-	dput(dentry);
-	nfsd_nr_put++;
-}
-
-/*
- * Look for an empty slot, or select one to expire.
- */
-static void expire_slot(int cache)
-{
-	struct fh_entry *fhe, *empty = NULL;
-	unsigned long oldest = -1;
-	int i;
-
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (!fhe->dentry)
-			goto out;
-		if (fhe->reftime < oldest) {
-			oldest = fhe->reftime;
-			empty = fhe;
-		}
-	}
-	if (empty)
-		expire_fhe(empty, cache);
-
-out:
-	return;
-}
-
-/*
- * Expire any cache entries older than a certain age.
- */
-static void expire_old(int cache, int age)
-{
-	struct fh_entry *fhe;
-	int i;
-
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("expire_old: expiring %s older than %d\n",
-(cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
-#endif
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (!fhe->dentry)
-			continue;
-		if ((jiffies - fhe->reftime) > age)
-			expire_fhe(fhe, cache);
-	}
-
-	/*
-	 * Trim the fixup cache ...
-	 */
-	while (nfsd_nr_fixups > NFSD_MAX_FIXUPS) {
-		struct nfsd_fixup *fp;
-		fp = list_entry(fixup_head.prev, struct nfsd_fixup, lru);
-		if ((jiffies - fp->reftime) < NFSD_MAX_FIXUP_AGE)
-			break;
-		free_fixup_entry(fp);
-	}
-
-	/*
-	 * Trim the path cache ...
-	 */
-	while (nfsd_nr_paths > NFSD_MAX_PATHS) {
-		struct nfsd_path *pe;
-		pe = list_entry(path_inuse.prev, struct nfsd_path, lru);
-		if (pe->users)
-			break;
-		free_path_entry(pe);
-	}
-}
-
-/*
- * Add a dentry to the file or dir cache.
- *
- * Note: As NFS file handles must have an inode, we don't accept
- * negative dentries.
- */
-static int add_to_fhcache(struct dentry *dentry, int cache)
-{
-	struct fh_entry *fhe, *empty = NULL;
-	struct inode *inode = dentry->d_inode;
-
+	struct inode *inode;
+	struct list_head *lp;
+	struct dentry *result;
+	inode = iget_in_use(sb, ino);
 	if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("add_to_fhcache: %s/%s rejected, no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-		return 0;
-	}
+		dprintk("nfsd_iget: failed to find ino: %lu on %s\n",
+			ino, bdevname(sb->s_dev));
+		return ERR_PTR(-ESTALE);
+	}
+	if (is_bad_inode(inode)
+	    || (generation && inode->i_generation != generation)
+		) {
+		/* we didn't find the right inode.. */
+		dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
+			inode->i_ino,
+			inode->i_nlink, inode->i_count,
+			inode->i_generation,
+			generation);
 
-repeat:
-	fhe = find_fhe(dentry, cache, &empty);
-	if (fhe) {
-		return 0;
+		iput(inode);
+		return ERR_PTR(-ESTALE);
 	}
-
-	/*
-	 * Not found ... make a new entry.
+	/* now to find a dentry.
+	 * If possible, get a well-connected one
 	 */
-	if (empty) {
-		empty->dentry = dentry;
-		empty->reftime = jiffies;
-		empty->ino = inode->i_ino;
-		empty->dev = inode->i_dev;
-		return 1;
-	}
-
-	/* if nfsd_server is zero, NFSD_MAXFH will be zero too, so
-	 * find_fhe() will NEVER find the file handle NOR an empty space,
-	 * and expire_slot will not be able to expire any file handle,
-	 * because NFSD_MAXFH is zero ... */
-
-	if (nfsd_nservers <= 0) {
-		return 0;
-	}
-
-	expire_slot(cache);
-	goto repeat;
-}
-
-/*
- * Find an entry in the dir cache for the specified inode number.
- */
-static struct fh_entry *find_fhe_by_ino(kdev_t dev, ino_t ino)
-{
-	struct fh_entry * fhe = &dirstable[0];
-	int i;
-
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (fhe->ino == ino && fhe->dev == dev) {
-			fhe->reftime = jiffies;
-			return fhe;
+	for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+		result = list_entry(lp,struct dentry, d_alias);
+		if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+			dget(result);
+			iput(inode);
+			return result;
 		}
 	}
-	return NULL;
+	result = d_alloc_root(inode, NULL);
+	if (result == NULL) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+	result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+	d_rehash(result); /* so a dput won't loose it */
+	return result;
 }
 
-/*
- * Find the (directory) dentry with the specified (dev, inode) number.
- * Note: this leaves the dentry in the cache.
+/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
+ * as a parent and "name" as a name
+ * It should possibly go in dcache.c
  */
-static struct dentry *find_dentry_by_ino(kdev_t dev, ino_t ino)
+int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
 {
-	struct fh_entry *fhe;
-	struct nfsd_path *pe;
-	struct dentry * dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_dentry_by_ino: looking for inode %ld\n", ino);
-#endif
-	/*
-	 * Special case: inode number 2 is the root inode,
-	 * so we can use the root dentry for the device.
-	 */
-	if (ino == 2) {
-		struct super_block *sb = get_super(dev);
-		if (sb) {
+	struct dentry *tdentry;
 #ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: getting root dentry for %s\n", kdevname(dev));
-#endif
-			if (sb->s_root) {
-				dentry = dget(sb->s_root);
-				goto out;
-			} else {
+	if (!IS_ROOT(target))
+		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
+	if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
+		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
+#endif
+	name->hash = full_name_hash(name->name, name->len);
+	tdentry = d_alloc(parent, name);
+	if (tdentry == NULL)
+		return -ENOMEM;
+	d_move(target, tdentry);
+
+	/* tdentry will have been made a "child" of target (the parent of target)
+	 * make it an IS_ROOT instead
+	 */
+	list_del(&tdentry->d_child);
+	tdentry->d_parent = tdentry;
+	d_rehash(target);
+	dput(tdentry);
+
+	/* if parent is properly connected, then we can assert that
+	 * the children are connected, but it must be a singluar (non-forking)
+	 * branch
+	 */
+	if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+		while (target) {
+			target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+			parent = target;
+			if (list_empty(&parent->d_subdirs))
+				target = NULL;
+			else {
+				target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
 #ifdef NFSD_PARANOIA
-				printk("find_dentry_by_ino: %s has no root??\n",
-					kdevname(dev));
+				/* must be only child */
+				if (target->d_child.next != &parent->d_subdirs
+				    || target->d_child.prev != &parent->d_subdirs)
+					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
+					       parent->d_name.name, target->d_name.name);
 #endif
 			}
 		}
 	}
+	return 0;
+}
 
-	/*
-	 * Search the dentry cache ...
-	 */
-	fhe = find_fhe_by_ino(dev, ino);
-	if (fhe) {
-		dentry = dget(fhe->dentry);
-		goto out;
-	}
-	/*
-	 * Search the path cache ...
-	 */
-	dentry = NULL;
-	pe = get_path_entry(dev, ino);
-	if (pe) {
-		struct dentry *res;
-		res = lookup_dentry(pe->name, NULL, 0);
-		if (!IS_ERR(res)) {
-			struct inode *inode = res->d_inode;
-			if (inode && inode->i_ino == ino &&
-				     inode->i_dev == dev) {
-				dentry = res;
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: found %s/%s, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ino);
-#endif
-				if (add_to_fhcache(dentry, NFSD_DIR_CACHE)) {
-					dget(dentry);
-					nfsd_nr_verified++;
-				}
-				put_path(pe);
-			} else {
-				dput(res);
-				put_path(pe);
-				/* We should delete it from the cache. */
-				free_path_entry(pe);
+/* this routine finds the dentry of the parent of a given directory
+ * it should be in the filesystem accessed by nfsd_operations
+ * it assumes lookup("..") works.
+ */
+struct dentry *nfsd_findparent(struct dentry *child)
+{
+	struct dentry *tdentry, *pdentry;
+	tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
+	if (!tdentry)
+		return ERR_PTR(-ENOMEM);
+
+	/* I'm going to assume that if the returned dentry is different, then
+	 * it is well connected.  But nobody returns different dentrys do they?
+	 */
+	pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
+	d_drop(tdentry); /* we never want ".." hashed */
+	if (!pdentry) {
+		/* I don't want to return a ".." dentry.
+		 * I would prefer to return an unconnected "IS_ROOT" dentry,
+		 * though a properly connected dentry is even better
+		 */
+		/* if first or last of alias list is not tdentry, use that
+		 * else make a root dentry
+		 */
+		struct list_head *aliases = &tdentry->d_inode->i_dentry;
+		if (aliases->next != aliases) {
+			pdentry = list_entry(aliases->next, struct dentry, d_alias);
+			if (pdentry == tdentry)
+				pdentry = list_entry(aliases->prev, struct dentry, d_alias);
+			if (pdentry == tdentry)
+				pdentry = NULL;
+			if (pdentry) dget(pdentry);
+		}
+		if (pdentry == NULL) {
+			pdentry = d_alloc_root(igrab(tdentry->d_inode), NULL);
+			if (pdentry) {
+				pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+				d_rehash(pdentry);
 			}
-		} else {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: %s lookup failed\n", pe->name);
-#endif
-			put_path(pe);
-			/* We should delete it from the cache. */
-			free_path_entry(pe);
 		}
+		if (pdentry == NULL)
+			pdentry = ERR_PTR(-ENOMEM);
 	}
-out:
-	return dentry;
+	dput(tdentry); /* it is not hashed, it will be discarded */
+	return pdentry;
 }
 
-/*
- * Look for an entry in the file cache matching the dentry pointer,
- * and verify that the (dev, inode) numbers are correct. If found,
- * the entry is removed from the cache.
- */
-static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
+static struct dentry *splice(struct dentry *child, struct dentry *parent)
 {
-/* FIXME: this must use the dev/ino/dir_ino triple. */ 
-#if 0
-	struct fh_entry * fhe;
-
-	fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
-	if (fhe) {
-		struct dentry *parent, *dentry;
-		struct inode *inode;
-
-		dentry = fhe->dentry;
-		inode = dentry->d_inode;
+	int err = 0;
+	struct qstr qs;
+	char namebuf[256];
+	struct list_head *lp;
+	struct dentry *tmp;
+	/* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
+	 * it should be a child of parent.
+	 * We see if we can find a name and, if we can - splice it in.
+	 * We hold the i_sem on the parent the whole time to try to follow 
+	 * locking protocols.
+	 */
+	qs.name = namebuf;
+	down(&parent->d_inode->i_sem);
 
-		if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_in_fhcache: %s/%s has no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
+	/* Now, things might have changed while we waited.
+	 * Possibly a friendly filesystem found child and spliced it in in 
+	 * response to a lookup (though nobody does this yet).  
+	 * In this case, just succeed.
+	 */
+	if (child->d_parent == parent) goto out;
+	/* Possibly a new dentry has been made for this child->d_inode in 
+	 * parent by a lookup.  In this case return that dentry. 
+	 * caller must notice and act accordingly
+	 */
+	for (lp = child->d_inode->i_dentry.next; lp != &child->d_inode->i_dentry ; lp=lp->next) {
+		tmp = list_entry(lp,struct dentry, d_alias);
+		if (tmp->d_parent == parent) {
+			child = dget(tmp);
 			goto out;
 		}
-		if (inode->i_ino != u32_to_ino_t(fh->fh_ino))
-			goto out;
- 		if (inode->i_dev != u32_to_kdev_t(fh->fh_dev))
-			goto out;
-
-		fhe->dentry = NULL;
-		fhe->ino = 0;
-		fhe->dev = 0;
-		nfsd_nr_put++;
-		/*
-		 * Make sure the parent is in the dir cache ...
-		 */
-		parent = dget(dentry->d_parent);
-		if (add_to_fhcache(parent, NFSD_DIR_CACHE))
-			nfsd_nr_verified++;
-		else
-			dput(parent);
-		return dentry;
 	}
-out:
-#endif
-	return NULL;
-}
-
-/*
- * Look for an entry in the parent directory with the specified
- * inode number.
- */
-static struct dentry *lookup_by_inode(struct dentry *parent, ino_t ino)
-{
-	struct dentry *dentry;
-	int error;
-	struct nfsd_dirent dirent;
-
-	/*
-	 * Search the directory for the inode number.
+	/* well, if we can find a name for child in parent, it should be 
+	 * safe to splice it in 
 	 */
-	dirent.ino = ino;
-	error = get_parent_ino(parent, &dirent);
-	if (error) {
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name);
-#endif
-		goto no_entry;
-	}
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: found %s\n", dirent.name);
-#endif
-
-	dentry = lookup_dentry(dirent.name, parent, 0);
-	if (!IS_ERR(dentry)) {
-		if (dentry->d_inode && dentry->d_inode->i_ino == ino)
-			goto out;
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: %s/%s inode mismatch??\n",
-parent->d_name.name, dentry->d_name.name);
-#endif
-		dput(dentry);
-	} else {
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: %s lookup failed, error=%ld\n",
-dirent.name, PTR_ERR(dentry));
-#endif
+	err = get_ino_name(parent, &qs, child->d_inode->i_ino);
+	if (err)
+		goto out;
+	tmp = d_lookup(parent, &qs);
+	if (tmp) {
+		/* Now that IS odd.  I wonder what it means... */
+		err = -EEXIST;
+		printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);
+		dput(tmp);
+		goto out;
 	}
-
-no_entry:
-	dentry = NULL;
-out:
-	return dentry;
+	err = d_splice(child, parent, &qs);
+	dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino);
+ out:
+	up(&parent->d_inode->i_sem);
+	if (err)
+		return ERR_PTR(err);
+	else
+		return child;
 }
 
 /*
- * Search the fix-up list for a dentry from a prior lookup.
+ * This is the basic lookup mechanism for turning an NFS file handle
+ * into a dentry.
+ * We use nfsd_iget and if that doesn't return a suitably connected dentry,
+ * we try to find the parent, and the parent of that and so-on until a
+ * connection if made.
  */
-static ino_t nfsd_cached_lookup(struct knfs_fh *fh)
-{
-	struct nfsd_fixup *fp;
-
-	fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev),
-				u32_to_ino_t(fh->fh_dirino),
-				u32_to_ino_t(fh->fh_ino));
-	if (fp)
-		return fp->new_dirino;
-	return 0;
-}
-
-void
-expire_all(void)
+static struct dentry *
+find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
 {
- 	if (time_after_eq(jiffies, nfsd_next_expire)) {
- 		expire_old(NFSD_FILE_CACHE,  5*HZ);
- 		expire_old(NFSD_DIR_CACHE , 60*HZ);
- 		nfsd_next_expire = jiffies + 5*HZ;
- 	}
-}
+	struct dentry *dentry, *result = NULL;
+	struct dentry *tmp;
+	int  found =0;
+	int err;
+	/* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
+	 * dcache path ever exists, as otherwise two partial paths might get
+	 * joined together, which would be very confusing.
+	 * If there is ever an unconnected non-root directory, then this lock
+	 * must be held.
+	 */
 
-/* 
- * Free cache after unlink/rmdir.
- */
-void
-expire_by_dentry(struct dentry *dentry)
-{
-	struct fh_entry *fhe;
 
-	fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL);
-	if (fhe) {
-		expire_fhe(fhe, NFSD_FILE_CACHE);
-	}
-	fhe = find_fhe(dentry, NFSD_DIR_CACHE, NULL);
-	if (fhe) {
-		expire_fhe(fhe, NFSD_DIR_CACHE);
+	nfsdstats.fh_lookup++;
+	/*
+	 * Attempt to find the inode.
+	 */
+ retry:
+	result = nfsd_iget(sb, fh->fh_ino, fh->fh_generation);
+	err = PTR_ERR(result);
+	if (IS_ERR(result))
+		goto err_out;
+	err = -ESTALE;
+	if (!result) {
+		dprintk("find_fh_dentry: No inode found.\n");
+		goto err_out;
 	}
-}
+	if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
+		return result;
 
-/*
- * The is the basic lookup mechanism for turning an NFS file handle 
- * into a dentry. There are several levels to the search:
- * (1) Look for the dentry pointer the short-term fhcache,
- *     and verify that it has the correct inode number.
- *
- * (2) Try to validate the dentry pointer in the file handle,
- *     and verify that it has the correct inode number. If this
- *     fails, check for a cached lookup in the fix-up list and
- *     repeat step (2) using the new dentry pointer.
- *
- * (3) Look up the dentry by using the inode and parent inode numbers
- *     to build the name string. This should succeed for any Unix-like
- *     filesystem.
- *
- * (4) Search for the parent dentry in the dir cache, and then
- *     look for the name matching the inode number.
- *
- * (5) The most general case ... search the whole volume for the inode.
- *
- * If successful, we return a dentry with the use count incremented.
- *
- * Note: steps (4) and (5) above are probably unnecessary now that (3)
- * is working. Remove the code once this is verified ...
- */
-static struct dentry *
-find_fh_dentry(struct knfs_fh *fh)
-{
-	struct super_block *sb;
-	struct dentry *dentry, *parent;
-	struct inode * inode;
-	struct list_head *lst;
-	int looked_up = 0, retry = 0;
-	ino_t dirino;
+	/* result is now an anonymous dentry, which may be adequate as it 
+	 * stands, or else will get spliced into the dcache tree */
 
-	/*
-	 * Stage 1: Look for the dentry in the short-term fhcache.
-	 */
-	dentry = find_dentry_in_fhcache(fh);
-	if (dentry) {
-		nfsdstats.fh_cached++;
-		goto out;
+	if (!S_ISDIR(result->d_inode->i_mode) && ! needpath) {
+		nfsdstats.fh_anon++;
+		return result;
 	}
-	/*
-	 * Stage 2: Attempt to find the inode.
+
+	/* It's a directory, or we are required to confirm the file's
+	 * location in the tree.
 	 */
-	sb = get_super(fh->fh_dev);
-	if (NULL == sb) {
-		printk("find_fh_dentry: No SuperBlock for device %s.",
-		       kdevname(fh->fh_dev));
-		dentry = NULL;
-		goto out;
-	}
+	dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,fh->fh_ino);
+	down(&sb->s_nfsd_free_path_sem);
 
-	dirino = u32_to_ino_t(fh->fh_dirino);
-	inode = iget_in_use(sb, fh->fh_ino);
-	if (!inode) {
-		dprintk("find_fh_dentry: No inode found.\n");
-		goto out_five;
-	}
-	goto check;
-recheck:
-	if (!inode) {
-		dprintk("find_fh_dentry: No inode found.\n");
-		goto out_three;
+	/* claiming the semaphore might have allow things to get fixed up */
+	if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+		up(&sb->s_nfsd_free_path_sem);
+		return result;
 	}
-check:
-	for (lst = inode->i_dentry.next;
-	     lst != &inode->i_dentry;
-	     lst = lst->next) {
-		dentry = list_entry(lst, struct dentry, d_alias);
-
-/* if we are looking up a directory then we don't need the parent! */
-		if (!dentry ||
-		    !dentry->d_parent ||
-		    !dentry->d_parent->d_inode) {
-printk("find_fh_dentry: Found a useless inode %lu\n", inode->i_ino);
-			continue;
-		}
-		if (dentry->d_parent->d_inode->i_ino != dirino)
-			continue;
 
-		dget(dentry);
-		iput(inode);
-#ifdef NFSD_DEBUG_VERBOSE
-		printk("find_fh_dentry: Found%s %s/%s filehandle dirino = %lu, %lu\n",
-		       retry ? " Renamed" : "",
-		       dentry->d_parent->d_name.name,
-		       dentry->d_name.name,
-		       dentry->d_parent->d_inode->i_ino,
-		       dirino);
-#endif
-		goto out;
-	} /* for inode->i_dentry */
 
-	/*
-	 * Before proceeding to a lookup, check for a rename
-	 */
-	if (!retry && (dirino = nfsd_cached_lookup(fh))) {
-		dprintk("find_fh_dentry: retry with %lu\n", dirino);
-		retry = 1;
-		goto recheck;
+	found = 0;
+	if (!S_ISDIR(result->d_inode->i_mode)) {
+		nfsdstats.fh_nocache_nondir++;
+		if (fh->fh_dirino == 0)
+			goto err_result; /* don't know how to find parent */
+		else {
+			/* need to iget fh->fh_dirino and make sure this 
+			 * inode is in that directory 
+			 */
+			dentry = nfsd_iget(sb, fh->fh_dirino, 0);
+			err = PTR_ERR(dentry);
+			if (IS_ERR(dentry))
+				goto err_result;
+			err = -ESTALE;
+			if (!dentry->d_inode
+			    || !S_ISDIR(dentry->d_inode->i_mode)) {
+				goto err_dentry;
+			}
+			if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
+				found = 1;
+			tmp = splice(result, dentry);
+			err = PTR_ERR(tmp);
+			if (IS_ERR(tmp))
+				goto err_dentry;
+			if (tmp != result) {
+				/* it is safe to just use tmp instead, but 
+				 * we must discard result first 
+				 */
+				d_drop(result);
+				dput(result);
+				result = tmp;
+				/* If !found, then this is really wierd, 
+				 * but it shouldn't hurt */
+			}
+		}
+	} else {
+		nfsdstats.fh_nocache_dir++;
+		dentry = dget(result);
 	}
 
-	iput(inode);
+	while(!found) {
+		/* LOOP INVARIANT */
+		/* haven't found a place in the tree yet, but we do have a 
+		 * free path from dentry down to result, and dentry is a 
+		 * directory.  Have a hold on dentry and result 
+		 */
+		struct dentry *pdentry;
+		struct inode *parent;
 
-	dprintk("find_fh_dentry: dirino not found %lu\n", dirino);
+		pdentry = nfsd_findparent(dentry);
+		err = PTR_ERR(pdentry);
+		if (IS_ERR(pdentry))
+			goto err_dentry;
+		parent = pdentry->d_inode;
+		err = -EACCES;
+		if (!parent) {
+			dput(pdentry);
+			goto err_dentry;
+		}
 
-out_three:
+		if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
+			found = 1;
 
-	/*
-	 * Stage 3: Look up the dentry based on the inode and parent inode
-	 * numbers. This should work for all Unix-like filesystems.
-	 */
-	looked_up = 1;
-	dentry = lookup_inode(u32_to_kdev_t(fh->fh_dev),
-			      u32_to_ino_t(fh->fh_dirino),
-			      u32_to_ino_t(fh->fh_ino));
-	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: looked up %s/%s\n",
-       dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-		if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
-			nfsdstats.fh_lookup++;
-			goto out;
+		tmp = splice(dentry, pdentry);
+		if (tmp != dentry) {
+			/* Something wrong.  We need to drop the whole 
+			 * dentry->result path whatever it was
+			 */
+			struct dentry *d;
+			for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent)
+				d_drop(d);
+		}
+		if (IS_ERR(tmp)) {
+			err = PTR_ERR(tmp);
+			dput(pdentry);
+			goto err_dentry;
+		}
+		if (tmp != dentry) {
+			/* we lost a race,  try again
+			 */
+			dput(tmp);
+			dput(dentry);
+			dput(result);	/* this will discard the whole free path, so we can up the semaphore */
+			up(&sb->s_nfsd_free_path_sem);
+			goto retry;
 		}
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s lookup mismatch!\n",
-       dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
 		dput(dentry);
+		dentry = pdentry;
 	}
+	dput(dentry);
+	up(&sb->s_nfsd_free_path_sem);
+	return result;
 
-	/*
-	 * Stage 4: Look for the parent dentry in the fhcache ...
-	 */
-	parent = find_dentry_by_ino(u32_to_kdev_t(fh->fh_dev),
-				    u32_to_ino_t(fh->fh_dirino));
-	if (parent) {
-		/*
-		 * ... then search for the inode in the parent directory.
-		 */
-		dget(parent);
-		dentry = lookup_by_inode(parent, u32_to_ino_t(fh->fh_ino));
-		dput(parent);
-		if (dentry)
-			goto out;
-	}
-
-out_five:
-
-	/*
-	 * Stage 5: Search the whole volume, Yea Right.
-	 */
-#ifdef NFSD_PARANOIA_EXTREME
-printk("find_fh_dentry: %s/%u dir/%u not found!\n",
-       kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino, fh->fh_dirino);
-#endif
-	dentry = NULL;
-	nfsdstats.fh_stale++;
-	
-out:
-	expire_all();
-	return dentry;
+err_dentry:
+	dput(dentry);
+err_result:
+	dput(result);
+	up(&sb->s_nfsd_free_path_sem);
+err_out:
+	if (err == -ESTALE)
+		nfsdstats.fh_stale++;
+	return ERR_PTR(err);
 }
 
 /*
@@ -1117,6 +495,9 @@
  *
  * Note that the file handle dentry may need to be freed even after
  * an error return.
+ *
+ * This is only called at the start of an nfsproc call, so fhp points to
+ * a svc_fh which is all 0 except for the over-the-wire file handle.
  */
 u32
 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
@@ -1134,57 +515,79 @@
 		fh->fh_ino,
 		fh->fh_dirino);
 
-	if (fhp->fh_dverified)
-		goto check_type;
-	/*
-	 * Look up the export entry.
-	 */
-	error = nfserr_stale;
-	exp = exp_get(rqstp->rq_client,
-		      u32_to_kdev_t(fh->fh_xdev),
-		      u32_to_ino_t(fh->fh_xino));
-	if (!exp) {
-		/* export entry revoked */
-		nfsdstats.fh_stale++;
-		goto out;
-	}
+	if (!fhp->fh_dentry) {
+		/*
+		 * Security: Check that the fh is internally consistant 
+		 * (from <gam3@acm.org>)
+		 */
+		if (fh->fh_dev != fh->fh_xdev) {
+			printk("fh_verify: Security: export on other device (%s, %s).\n",
+			       kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
+			error = nfserr_stale;
+			nfsdstats.fh_stale++;
+			goto out;
+		}
 
-	/* Check if the request originated from a secure port. */
-	error = nfserr_perm;
-	if (!rqstp->rq_secure && EX_SECURE(exp)) {
-		printk(KERN_WARNING
-			"nfsd: request from insecure port (%08x:%d)!\n",
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port));
-		goto out;
-	}
+		/*
+		 * Look up the export entry.
+		 */
+		error = nfserr_stale; 
+		exp = exp_get(rqstp->rq_client,
+			      u32_to_kdev_t(fh->fh_xdev),
+			      u32_to_ino_t(fh->fh_xino));
+		if (!exp) {
+			/* export entry revoked */
+			nfsdstats.fh_stale++;
+			goto out;
+		}
+
+		/* Check if the request originated from a secure port. */
+		error = nfserr_perm;
+		if (!rqstp->rq_secure && EX_SECURE(exp)) {
+			printk(KERN_WARNING
+			       "nfsd: request from insecure port (%08x:%d)!\n",
+			       ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			       ntohs(rqstp->rq_addr.sin_port));
+			goto out;
+		}
 
-	/* Set user creds if we haven't done so already. */
-	nfsd_setuser(rqstp, exp);
+		/* Set user creds if we haven't done so already. */
+		nfsd_setuser(rqstp, exp);
 
-	/*
-	 * Look up the dentry using the NFS file handle.
-	 */
-	error = nfserr_noent;
-	dentry = find_fh_dentry(fh);
-	if (!dentry) {
-		goto out;
-	}
-	if (IS_ERR(dentry)) {
-		error = nfserrno(-PTR_ERR(dentry));
-		goto out;
+		/*
+		 * Look up the dentry using the NFS file handle.
+		 */
+
+		dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+					fh,
+					!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+
+		if (IS_ERR(dentry)) {
+			error = nfserrno(-PTR_ERR(dentry));
+			goto out;
+		}
+#ifdef NFSD_PARANOIA
+		if (S_ISDIR(dentry->d_inode->i_mode) &&
+		    (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+			printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+			       dentry->d_parent->d_name.name, dentry->d_name.name);
+		}
+#endif
+
+		fhp->fh_dentry = dentry;
+		fhp->fh_export = exp;
+		nfsd_nr_verified++;
+	} else {
+		/* just rechecking permissions
+		 * (e.g. nfsproc_create calls fh_verify, then nfsd_create 
+		 * does as well)
+		 */
+		dprintk("nfsd: fh_verify - just checking\n");
+		dentry = fhp->fh_dentry;
+		exp = fhp->fh_export;
 	}
 
-	/*
-	 * Note:  it's possible the returned dentry won't be the one in the
-	 * file handle.  We can correct the file handle for our use, but
-	 * unfortunately the client will keep sending the broken one.  Let's
-	 * hope the lookup will keep patching things up.
-	 */
-	fhp->fh_dentry = dentry;
-	fhp->fh_export = exp;
-	fhp->fh_dverified = 1;
-	nfsd_nr_verified++;
+	inode = dentry->d_inode;
 
 	/* Type check. The correct error return for type mismatches
 	 * does not seem to be generally agreed upon. SunOS seems to
@@ -1192,39 +595,7 @@
 	 * spec says this is incorrect (implementation notes for the
 	 * write call).
 	 */
-check_type:
-	dentry = fhp->fh_dentry;
-	inode = dentry->d_inode;
-	error = nfserr_stale;
-	/* On a heavily loaded SMP machine, more than one identical
-	   requests may run at the same time on different processors.
-	   One thread may get here with unfinished fh after another
-	   thread just fetched the inode. It doesn't make any senses
-	   to check fh->fh_generation here since it has not been set
-	   yet. In that case, we shouldn't send back the stale
-	   filehandle to the client. We use fh->fh_dcookie to indicate
-	   if fh->fh_generation is set or not. If fh->fh_dcookie is
-	   not set, don't return stale filehandle. */
-	if (inode->i_generation != fh->fh_generation) {
-		if (fh->fh_dcookie) {
-			dprintk("fh_verify: Bad version %lu %u %u: 0x%x, 0x%x\n",
-				inode->i_ino,
-				inode->i_generation,
-				fh->fh_generation,
-				type, access);
-			nfsdstats.fh_stale++;
-			goto out;
-		}
-		else {
-			/* We get here when inode is fetched by other
-			   threads. We just use what is in there. */
-			fh->fh_ino = ino_t_to_u32(inode->i_ino);
-			fh->fh_generation = inode->i_generation;
-			fh->fh_dcookie = (struct dentry *)0xfeebbaca;
-			nfsdstats.fh_concurrent++;
-		}
-	}
-	exp = fhp->fh_export;
+
 	if (type > 0 && (inode->i_mode & S_IFMT) != type) {
 		error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
 		goto out;
@@ -1238,38 +609,37 @@
 	 * Security: Check that the export is valid for dentry <gam3@acm.org>
 	 */
 	error = 0;
-	if (fh->fh_dev != fh->fh_xdev) {
-		printk("fh_verify: Security: export on other device (%s, %s).\n",
-		       kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
-		error = nfserr_stale;
-		nfsdstats.fh_stale++;
-	} else if (exp->ex_dentry != dentry) {
-		struct dentry *tdentry = dentry;
 
-		do {
-			tdentry = tdentry->d_parent;
-			if (exp->ex_dentry == tdentry)
-				break;
-			/* executable only by root and we can't be root */
-			if (current->fsuid
-			    && !(tdentry->d_inode->i_uid
-			         && (tdentry->d_inode->i_mode & S_IXUSR))
-			    && !(tdentry->d_inode->i_gid
-				 && (tdentry->d_inode->i_mode & S_IXGRP))
-			    && !(tdentry->d_inode->i_mode & S_IXOTH)
-			    && (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
+	if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+		if (exp->ex_dentry != dentry) {
+			struct dentry *tdentry = dentry;
+
+			do {
+				tdentry = tdentry->d_parent;
+				if (exp->ex_dentry == tdentry)
+					break;
+				/* executable only by root and we can't be root */
+				if (current->fsuid
+				    && (exp->ex_flags & NFSEXP_ROOTSQUASH)
+				    && !(tdentry->d_inode->i_uid
+					 && (tdentry->d_inode->i_mode & S_IXUSR))
+				    && !(tdentry->d_inode->i_gid
+					 && (tdentry->d_inode->i_mode & S_IXGRP))
+				    && !(tdentry->d_inode->i_mode & S_IXOTH)
+					) {
+					error = nfserr_stale;
+					nfsdstats.fh_stale++;
+					dprintk("fh_verify: no root_squashed access.\n");
+				}
+			} while ((tdentry != tdentry->d_parent));
+			if (exp->ex_dentry != tdentry) {
 				error = nfserr_stale;
 				nfsdstats.fh_stale++;
-dprintk("fh_verify: no root_squashed access.\n");
+				printk("nfsd Security: %s/%s bad export.\n",
+				       dentry->d_parent->d_name.name,
+				       dentry->d_name.name);
+				goto out;
 			}
-		} while ((tdentry != tdentry->d_parent));
-		if (exp->ex_dentry != tdentry) {
-			error = nfserr_stale;
-			nfsdstats.fh_stale++;
-			printk("nfsd Security: %s/%s bad export.\n",
-			       dentry->d_parent->d_name.name,
-			       dentry->d_name.name);
-			goto out;
 		}
 	}
 
@@ -1277,10 +647,11 @@
 	if (!error) {
 		error = nfsd_permission(exp, dentry, access);
 	}
-#ifdef NFSD_PARANOIA
-if (error)
-printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+#ifdef NFSD_PARANOIA_EXTREME
+	if (error) {
+		printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+	}
 #endif
 out:
 	return error;
@@ -1300,7 +671,7 @@
 	struct dentry *parent = dentry->d_parent;
 
 	dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
-		exp->ex_dev, exp->ex_ino,
+		exp->ex_dev, (long) exp->ex_ino,
 		parent->d_name.name, dentry->d_name.name,
 		(inode ? inode->i_ino : 0));
 
@@ -1309,33 +680,33 @@
 	 * may not be done on error paths, but the cleanup must call fh_put.
 	 * Fix this soon!
 	 */
-	if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) {
+	if (fhp->fh_dentry || fhp->fh_locked) {
 		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
 			parent->d_name.name, dentry->d_name.name);
 	}
 	fh_init(fhp);
 
-	fhp->fh_handle.fh_dcookie = dentry;
+	fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
+	fhp->fh_handle.fh_dev    = kdev_t_to_u32(parent->d_inode->i_dev);
+	fhp->fh_handle.fh_xdev   = kdev_t_to_u32(exp->ex_dev);
+	fhp->fh_handle.fh_xino   = ino_t_to_u32(exp->ex_ino);
+	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
 	if (inode) {
 		fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 		fhp->fh_handle.fh_generation = inode->i_generation;
-		fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
+		if (S_ISDIR(inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
+			fhp->fh_handle.fh_dirino = 0;
 	}
-	fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
-	fhp->fh_handle.fh_dev	 = kdev_t_to_u32(parent->d_inode->i_dev);
-	fhp->fh_handle.fh_xdev	 = kdev_t_to_u32(exp->ex_dev);
-	fhp->fh_handle.fh_xino	 = ino_t_to_u32(exp->ex_ino);
 
 	fhp->fh_dentry = dentry; /* our internal copy */
 	fhp->fh_export = exp;
 
-	/* We stuck it there, we know it's good. */
-	fhp->fh_dverified = 1;
 	nfsd_nr_verified++;
 }
 
 /*
  * Update file handle information after changing a dentry.
+ * This is only called by nfsd_create
  */
 void
 fh_update(struct svc_fh *fhp)
@@ -1343,7 +714,7 @@
 	struct dentry *dentry;
 	struct inode *inode;
 
-	if (!fhp->fh_dverified)
+	if (!fhp->fh_dentry)
 		goto out_bad;
 
 	dentry = fhp->fh_dentry;
@@ -1352,7 +723,9 @@
 		goto out_negative;
 	fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 	fhp->fh_handle.fh_generation = inode->i_generation;
-	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
+	if (S_ISDIR(inode->i_mode) || (fhp->fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
+		fhp->fh_handle.fh_dirino = 0;
+
 out:
 	return;
 
@@ -1366,22 +739,19 @@
 }
 
 /*
- * Release a file handle.  If the file handle carries a dentry count,
- * we add the dentry to the short-term cache rather than release it.
+ * Release a file handle.
  */
 void
 fh_put(struct svc_fh *fhp)
 {
 	struct dentry * dentry = fhp->fh_dentry;
-	if (fhp->fh_dverified) {
+	if (dentry) {
 		fh_unlock(fhp);
-		fhp->fh_dverified = 0;
+		fhp->fh_dentry = NULL;
 		if (!dentry->d_count)
 			goto out_bad;
-		if (!dentry->d_inode || !add_to_fhcache(dentry, 0)) {
-			dput(dentry);
-			nfsd_nr_put++;
-		}
+		dput(dentry);
+		nfsd_nr_put++;
 	}
 	return;
 
@@ -1389,118 +759,4 @@
 	printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 	return;
-}
-
-/*
- * Flush any cached dentries for the specified device
- * or for all devices.
- *
- * This is called when revoking the last export for a
- * device, so that it can be unmounted cleanly.
- */
-void nfsd_fh_flush(kdev_t dev)
-{
-	struct fh_entry *fhe;
-	int i, pass = 2;
-
-	fhe = &filetable[0];
-	while (pass--) {
-		for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-			struct dentry *dentry = fhe->dentry;
-			if (!dentry)
-				continue;
-			if (dev && dentry->d_inode->i_dev != dev)
-				continue;
-			fhe->dentry = NULL;
-			dput(dentry);
-			nfsd_nr_put++;
-		}
-		fhe = &dirstable[0];
-	}
-}
-
-/*
- * Free the rename and path caches.
- */
-void nfsd_fh_free(void)
-{
-	struct list_head *tmp;
-	int i;
-
-	/* Flush dentries for all devices */
-	nfsd_fh_flush(0);
-
-	/*
-	 * N.B. write a destructor for these lists ...
-	 */
-	i = 0;
-	while ((tmp = fixup_head.next) != &fixup_head) {
-		struct nfsd_fixup *fp;
-		fp = list_entry(tmp, struct nfsd_fixup, lru);
-		free_fixup_entry(fp);
-		i++;
-	}
-	printk(KERN_DEBUG "nfsd_fh_free: %d fixups freed\n", i);
-
-	i = 0;
-	while ((tmp = path_inuse.next) != &path_inuse) {
-		struct nfsd_path *pe;
-		pe = list_entry(tmp, struct nfsd_path, lru);
-		free_path_entry(pe);
-		i++;
-	}
-	printk(KERN_DEBUG "nfsd_fh_free: %d paths freed\n", i);
-
-	printk(KERN_DEBUG "nfsd_fh_free: verified %d, put %d\n",
-		nfsd_nr_verified, nfsd_nr_put);
-}
-
-void nfsd_fh_init(void)
-{
-	extern void __my_nfsfh_is_too_big(void); 
-
-	if (filetable)
-		return;
-
-	/* Sanity check */ 
-	if (sizeof(struct nfs_fhbase) > 32) 
-		__my_nfsfh_is_too_big(); 
-
-	filetable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
-			    GFP_KERNEL);
-	dirstable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
-			    GFP_KERNEL);
-
-	if (filetable == NULL || dirstable == NULL) {
-		printk(KERN_WARNING "nfsd_fh_init : Could not allocate fhcache\n");
-		nfsd_nservers = 0;
-		return;
-	}
-
-	memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
-	memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
-	INIT_LIST_HEAD(&path_inuse);
-	INIT_LIST_HEAD(&fixup_head);
-
-	printk(KERN_DEBUG 
-		"nfsd_fh_init : initialized fhcache, entries=%lu\n", NFSD_MAXFH);
-	/*
-	 * Display a warning if the ino_t is larger than 32 bits.
-	 */
-	if (sizeof(ino_t) > sizeof(__u32))
-		printk(KERN_INFO 
-			"NFSD: ino_t is %d bytes, using lower 4 bytes\n",
-			sizeof(ino_t));
-}
-
-void
-nfsd_fh_shutdown(void)
-{
-	if (!filetable)
-		return;
-	printk(KERN_DEBUG 
-		"nfsd_fh_shutdown : freeing %ld fhcache entries.\n", NFSD_MAXFH);
-	kfree(filetable);
-	kfree(dirstable);
-	filetable = dirstable = NULL;
 }
diff -urN linux-2.2.18-pre15/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- linux-2.2.18-pre15/fs/nfsd/nfsproc.c	Wed May  3 17:16:46 2000
+++ linux/fs/nfsd/nfsproc.c	Mon Dec  3 20:34:12 2001
@@ -1,7 +1,10 @@
 /*
  * nfsproc2.c	Process version 2 NFS requests.
+ * linux/fs/nfsd/nfs2proc.c
+ * 
+ * Process version 2 NFS requests.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/linkage.h>
@@ -27,6 +30,9 @@
 
 #define NFSDDBG_FACILITY		NFSDDBG_PROC
 
+/* Check for dir entries '.' and '..' */
+#define isdotent(n, l)	(l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
+
 #define RETURN(st)	return st
 
 static void
@@ -144,7 +150,7 @@
 				ntohl(rqstp->rq_addr.sin_addr.s_addr),
 				ntohs(rqstp->rq_addr.sin_port),
 				argp->count);
-		argp->count = avail;
+		argp->count = avail << 2;
 	}
 
 	resp->count = argp->count;
@@ -211,6 +217,12 @@
 	} else if (nfserr)
 		goto done;
 
+	nfserr = nfserr_acces;
+	if (!argp->len)
+		goto done;
+	nfserr = nfserr_exist;
+	if (isdotent(argp->name, argp->len))
+		goto done;
 	/*
 	 * Do a lookup to verify the new file handle.
 	 */
@@ -223,7 +235,7 @@
 		 * whether the file exists or not. Time to bail ...
 		 */
 		nfserr = nfserr_acces;
-		if (!newfhp->fh_dverified) {
+		if (!newfhp->fh_dentry) {
 			printk(KERN_WARNING 
 				"nfsd_proc_create: file handle not verified\n");
 			goto done;
@@ -261,7 +273,7 @@
 		goto out_unlock;
 
 	attr->ia_valid |= ATTR_MODE;
-	attr->ia_mode = type | mode;
+	attr->ia_mode = mode;
 
 	/* Special treatment for non-regular files according to the
 	 * gospel of sun micro
@@ -279,22 +291,21 @@
 			type = S_IFIFO;
 		} else if (size != rdev) {
 			/* dev got truncated because of 16bit Linux dev_t */
-			nfserr = nfserr_io;	/* or nfserr_inval? */
+			nfserr = nfserr_inval;
 			goto out_unlock;
 		} else {
 			/* Okay, char or block special */
 			is_borc = 1;
 		}
 
+		/* we've used the SIZE information, so discard it */
+		attr->ia_valid &= ~ATTR_SIZE;
+
 		/* Make sure the type and device matches */
 		nfserr = nfserr_exist;
 		if (inode && (type != (inode->i_mode & S_IFMT) || 
 		    (is_borc && inode->i_rdev != rdev)))
 			goto out_unlock;
-
-		/* invalidate size because only (type == S_IFREG) has
-		   size. */
-		attr->ia_valid &= ~ATTR_SIZE;
 	}
 	
 	nfserr = 0;
@@ -388,11 +399,8 @@
 	 */
 	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
 						 argp->tname, argp->tlen,
-						 &newfh);
-	if (!nfserr) {
-		argp->attrs.ia_valid &= ~ATTR_SIZE;
-		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
-	}
+				 		 &newfh, &argp->attrs);
+
 
 	fh_put(&argp->ffh);
 	fh_put(&newfh);
@@ -411,7 +419,7 @@
 
 	dprintk("nfsd: MKDIR    %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
-	if (resp->fh.fh_dverified) {
+	if (resp->fh.fh_dentry) {
 		printk(KERN_WARNING
 			"nfsd_proc_mkdir: response already verified??\n");
 	}
@@ -446,8 +454,8 @@
 nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
 					  struct nfsd_readdirres  *resp)
 {
-	u32 *	buffer;
-	int	nfserr, count;
+	u32 *		buffer;
+	int		nfserr, count;
 
 	dprintk("nfsd: READDIR  %d/%d %d bytes at %d\n",
 		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
@@ -467,7 +475,8 @@
 
 	/* Read directory and encode entries on the fly */
 	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
-				nfssvc_encode_entry, buffer, &count);
+			      nfssvc_encode_entry,
+			      buffer, &count, NULL);
 	resp->count = count;
 
 	fh_put(&argp->fh);
@@ -547,6 +556,8 @@
 		{ NFSERR_NXIO, ENXIO },
 		{ NFSERR_ACCES, EACCES },
 		{ NFSERR_EXIST, EEXIST },
+		{ NFSERR_XDEV, EXDEV },
+		{ NFSERR_MLINK, EMLINK },
 		{ NFSERR_NODEV, ENODEV },
 		{ NFSERR_NOTDIR, ENOTDIR },
 		{ NFSERR_ISDIR, EISDIR },
@@ -554,39 +565,22 @@
 		{ NFSERR_FBIG, EFBIG },
 		{ NFSERR_NOSPC, ENOSPC },
 		{ NFSERR_ROFS, EROFS },
+		{ NFSERR_MLINK, EMLINK },
 		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
 		{ NFSERR_NOTEMPTY, ENOTEMPTY },
 #ifdef EDQUOT
 		{ NFSERR_DQUOT, EDQUOT },
 #endif
 		{ NFSERR_STALE, ESTALE },
-		{ NFSERR_WFLUSH, EIO },
 		{ -1, EIO }
 	};
 	int	i;
 
 	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
 		if (nfs_errtbl[i].syserr == errno)
-			return htonl (nfs_errtbl[i].nfserr);
+			return htonl(nfs_errtbl[i].nfserr);
 	}
 	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
 	return nfserr_io;
 }
 
-#if 0
-static void
-nfsd_dump(char *tag, u32 *buf, int len)
-{
-	int	i;
-
-	printk(KERN_NOTICE
-		"nfsd: %s (%d words)\n", tag, len);
-
-	for (i = 0; i < len && i < 32; i += 8)
-		printk(KERN_NOTICE
-			" %08lx %08lx %08lx %08lx"
-			" %08lx %08lx %08lx %08lx\n",
-			buf[i],   buf[i+1], buf[i+2], buf[i+3],
-			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
-}
-#endif
diff -urN linux-2.2.18-pre15/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- linux-2.2.18-pre15/fs/nfsd/nfssvc.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/nfsd/nfssvc.c	Mon Dec  3 20:34:12 2001
@@ -42,7 +42,7 @@
 extern struct svc_program	nfsd_program;
 static void			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot = { 0, 0 };
-static int			nfsd_active = 0;
+static atomic_t			nfsd_active = ATOMIC_INIT(0);
 
 /*
  * Maximum number of nfsd processes
@@ -55,24 +55,19 @@
 	struct svc_serv *	serv;
 	int			error;
 
-	dprintk("nfsd: creating service\n");
 	error = -EINVAL;
+	if (atomic_read(&nfsd_active))
+		goto out;	/* already running */
 	if (nrservs <= 0)
 		goto out;
 	if (nrservs > NFSD_MAXSERVS)
 		nrservs = NFSD_MAXSERVS;
+
+	dprintk("nfsd: creating service (%d)\n", nrservs);
 	nfsd_nservers = nrservs;
+	nfssvc_boot = xtime;
 
 	error = -ENOMEM;
-	nfsd_fh_init();		/* NFS dentry cache */
-	if (nfsd_nservers == 0)
-		goto out;
-	  
-	error = -ENOMEM;
-	nfsd_racache_init();     /* Readahead param cache */
-	if (nfsd_nservers == 0)
-		goto out;
-	  
 	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
 	if (serv == NULL)
 		goto out;
@@ -87,6 +82,8 @@
 		goto failure;
 #endif
 
+	nfsd_racache_init();	/* Readahead param cache */
+
 	while (nrservs--) {
 		error = svc_create_thread(nfsd, serv);
 		if (error < 0)
@@ -106,27 +103,27 @@
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
-	int		oldumask, err, first = 0;
+	int		err;
 
-	/* Lock module and set up kernel thread */
+	/* Lock module */
 	MOD_INC_USE_COUNT;
+
+	/* Set up kernel thread */
 	lock_kernel();
 	exit_mm(current);
+	sprintf(current->comm, "nfsd");
 	current->session = 1;
 	current->pgrp = 1;
+	current->fs->umask = 0;
+
+	/* Count active threads */
+	atomic_inc(&nfsd_active);
+
+	/* Start lockd */
+	lockd_up();
+
 	/* Let svc_process check client's authentication. */
 	rqstp->rq_auth = 1;
-	sprintf(current->comm, "nfsd");
-
-	oldumask = current->fs->umask;		/* Set umask to 0.  */
-	current->fs->umask = 0;
-	if (!nfsd_active++) {
-		nfssvc_boot = xtime;		/* record boot time */
-		first = 1;
-	}
-#if 0
-	lockd_up();				/* start lockd */
-#endif
 
 	/*
 	 * The main request loop
@@ -143,13 +140,8 @@
 		 * recvfrom routine.
 		 */
 		while ((err = svc_recv(serv, rqstp,
-		        first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
-			if (first && 1) {
-				exp_readlock();
-				expire_all();
-				exp_unlock();
-			}
-		}
+				       MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
+		    ;
 		if (err < 0)
 			break;
 
@@ -184,23 +176,17 @@
 		printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo);
 	}
 
-#if 0
+	/* Count active threads */
+	if (atomic_dec_and_test(&nfsd_active)) {
+		nfsd_export_shutdown();		/* revoke all exports */
+	        nfsd_racache_shutdown();	/* release read-ahead cache */
+	}
+
 	/* Release lockd */
 	lockd_down();
-#endif
-	if (!--nfsd_active) {
-		printk("nfsd: last server exiting\n");
-		/* revoke all exports */
-		nfsd_export_shutdown();
-		/* release fhcache */
-		nfsd_fh_shutdown ();
-		/* release read-ahead cache */
-	        nfsd_racache_shutdown();
-	}
 
-	/* Destroy the thread */
+	/* Destroy the thread's resources */
 	svc_exit_thread(rqstp);
-	current->fs->umask = oldumask;
 
 	/* Release module */
 	MOD_DEC_USE_COUNT;
@@ -213,7 +199,8 @@
 	kxdrproc_t		xdr;
 	u32			nfserr;
 
-	dprintk("nfsd_dispatch: proc %d\n", rqstp->rq_proc);
+	dprintk("nfsd_dispatch: vers %d proc %d\n",
+				rqstp->rq_vers, rqstp->rq_proc);
 	proc = rqstp->rq_procinfo;
 
 	/* Check whether we have this call in the cache. */
@@ -242,8 +229,20 @@
 		svc_putlong(&rqstp->rq_resbuf, nfserr);
 
 	/* Encode result.
-	 * FIXME: Most NFSv3 calls return wcc data even when the call failed
+	 * For NFSv2, additional info is never returned in case of an error.
 	 */
+#ifdef CONFIG_NFSD_V3
+	if (!(nfserr && rqstp->rq_vers == 2)) {
+		xdr = proc->pc_encode;
+		if (xdr && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+			/* Failed to encode result. Release cache entry */
+			dprintk("nfsd: failed to encode result!\n");
+			nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+			*statp = rpc_system_err;
+			return 1;
+		}
+	}
+#else
 	xdr = proc->pc_encode;
 	if (!nfserr && xdr
 	 && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
@@ -253,6 +252,7 @@
 		*statp = rpc_system_err;
 		return 1;
 	}
+#endif /* CONFIG_NFSD_V3 */
 
 	/* Store reply in cache. */
 	nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
@@ -262,16 +262,16 @@
 static struct svc_version	nfsd_version2 = {
 	2, 18, nfsd_procedures2, nfsd_dispatch
 };
-#ifdef CONFIG_NFSD_NFS3
+#ifdef CONFIG_NFSD_V3
 static struct svc_version	nfsd_version3 = {
-	3, 23, nfsd_procedures3, nfsd_dispatch
+	3, 22, nfsd_procedures3, nfsd_dispatch
 };
 #endif
 static struct svc_version *	nfsd_version[] = {
 	NULL,
 	NULL,
 	&nfsd_version2,
-#ifdef CONFIG_NFSD_NFS3
+#ifdef CONFIG_NFSD_V3
 	&nfsd_version3,
 #endif
 };
diff -urN linux-2.2.18-pre15/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr.c
--- linux-2.2.18-pre15/fs/nfsd/nfsxdr.c	Wed Nov 26 13:08:38 1997
+++ linux/fs/nfsd/nfsxdr.c	Mon Dec  3 20:34:12 2001
@@ -18,9 +18,14 @@
 #define NFSDDBG_FACILITY		NFSDDBG_XDR
 
 u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
-	nfserr_inval, nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
-	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
-	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
+	nfserr_acces, nfserr_exist, nfserr_xdev, nfserr_nodev,
+	nfserr_notdir, nfserr_isdir, nfserr_inval, nfserr_fbig,
+	nfserr_nospc, nfserr_rofs, nfserr_mlink,
+	nfserr_nametoolong, nfserr_notempty, nfserr_dquot, nfserr_stale,
+	nfserr_remote, nfserr_badhandle, nfserr_notsync,
+	nfserr_badcookie, nfserr_notsupp, nfserr_toosmall,
+	nfserr_serverfault, nfserr_badtype, nfserr_jukebox;
+
 
 #ifdef NFSD_OPTIMIZE_SPACE
 # define inline
@@ -52,18 +57,32 @@
 	nfserr_noent	= htonl(NFSERR_NOENT);
 	nfserr_io	= htonl(NFSERR_IO);
 	nfserr_inval	= htonl(NFSERR_INVAL);
-	nfserr_nxio = htonl(NFSERR_NXIO);
-	nfserr_acces = htonl(NFSERR_ACCES);
-	nfserr_exist = htonl(NFSERR_EXIST);
-	nfserr_nodev = htonl(NFSERR_NODEV);
-	nfserr_notdir = htonl(NFSERR_NOTDIR);
-	nfserr_isdir = htonl(NFSERR_ISDIR);
-	nfserr_fbig = htonl(NFSERR_FBIG);
-	nfserr_nospc = htonl(NFSERR_NOSPC);
-	nfserr_rofs = htonl(NFSERR_ROFS);
+	nfserr_nxio	= htonl(NFSERR_NXIO);
+	nfserr_acces	= htonl(NFSERR_ACCES);
+	nfserr_exist	= htonl(NFSERR_EXIST);
+	nfserr_xdev	= htonl(NFSERR_XDEV);
+	nfserr_nodev	= htonl(NFSERR_NODEV);
+	nfserr_notdir	= htonl(NFSERR_NOTDIR);
+	nfserr_isdir	= htonl(NFSERR_ISDIR);
+	nfserr_inval	= htonl(NFSERR_INVAL);
+	nfserr_fbig	= htonl(NFSERR_FBIG);
+	nfserr_nospc	= htonl(NFSERR_NOSPC);
+	nfserr_rofs	= htonl(NFSERR_ROFS);
+	nfserr_mlink	= htonl(NFSERR_MLINK);
 	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
-	nfserr_dquot = htonl(NFSERR_DQUOT);
-	nfserr_stale = htonl(NFSERR_STALE);
+	nfserr_notempty	= htonl(NFSERR_NOTEMPTY);
+	nfserr_dquot	= htonl(NFSERR_DQUOT);
+	nfserr_stale	= htonl(NFSERR_STALE);
+	nfserr_remote	= htonl(NFSERR_REMOTE);
+	nfserr_badhandle = htonl(NFSERR_BADHANDLE);
+	nfserr_notsync	= htonl(NFSERR_NOT_SYNC);
+	nfserr_badcookie = htonl(NFSERR_BAD_COOKIE);
+	nfserr_notsupp	= htonl(NFSERR_NOTSUPP);
+	nfserr_toosmall	= htonl(NFSERR_TOOSMALL);
+	nfserr_serverfault = htonl(NFSERR_SERVERFAULT);
+	nfserr_badtype	= htonl(NFSERR_BADTYPE);
+	nfserr_jukebox	= htonl(NFSERR_JUKEBOX);
+
 
 	inited = 1;
 }
diff -urN linux-2.2.18-pre15/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- linux-2.2.18-pre15/fs/nfsd/stats.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/nfsd/stats.c	Mon Dec  3 20:34:12 2001
@@ -32,16 +32,15 @@
 {
 	int	len;
 
-	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d %d\n",
+	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d\n",
 			nfsdstats.rchits,
 			nfsdstats.rcmisses,
 			nfsdstats.rcnocache,
-			nfsdstats.fh_cached,
-			nfsdstats.fh_valid,
-			nfsdstats.fh_fixup,
 			nfsdstats.fh_lookup,
-			nfsdstats.fh_stale,
-			nfsdstats.fh_concurrent);
+			nfsdstats.fh_anon,
+			nfsdstats.fh_nocache_nondir,
+			nfsdstats.fh_nocache_dir,
+			nfsdstats.fh_stale);
 
 	/* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
 	*eof = 0;
diff -urN linux-2.2.18-pre15/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.2.18-pre15/fs/nfsd/vfs.c	Tue Jan  4 10:12:23 2000
+++ linux/fs/nfsd/vfs.c	Mon Dec  3 20:34:12 2001
@@ -31,6 +31,10 @@
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
+#ifdef CONFIG_NFSD_V3
+#include <linux/nfs3.h>
+#include <linux/nfsd/xdr3.h>
+#endif /* CONFIG_NFSD_V3 */
 #include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 
@@ -41,16 +45,15 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FILEOP
 #define NFSD_PARANOIA
 
-/* Open mode for nfsd_open */
-#define OPEN_READ	0
-#define OPEN_WRITE	1
-
-/* Hack until we have a macro check for mandatory locks. */
-#ifndef IS_ISMNDLK
-#define IS_ISMNDLK(i)	(((i)->i_mode & (S_ISGID|S_IXGRP|S_IFMT)) \
-			 == (S_ISGID|S_IFREG))
-#endif
 
+/* We must ignore files (but only files) which might have mandatory
+ * locks on them because there is no way to know if the accesser has
+ * the lock.
+ */
+/* MANDATORY_LOCK taken from 2.3 */
+#define MANDATORY_LOCK(inode) \
+	(IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+#define IS_ISMNDLK(i)	(S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
 /* Time difference margin in seconds for comparison. It is a
    dynamically-tunable parameter via /proc/fs/nfs/time-diff-margin.
  */
@@ -83,65 +86,58 @@
 static struct raparms *		raparml = NULL;
 static struct raparms *		raparm_cache = NULL;
 
+
+/*
+ * We need to do a check-parent every time
+ * after we have locked the parent - to verify
+ * that the parent is still our parent and
+ * that we are still hashed onto it..
+ *
+ * This is required in case two processes race
+ * on removing (or moving) the same entry: the
+ * parent lock will serialize them, but the
+ * other process will be too late..
+ *
+ * Note that this nfsd_check_parent is identical
+ * the check_parent in linux/fs/namei.c.
+ */
+#define nfsd_check_parent(dir, dentry) \
+	((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
+
 /*
  * Lock a parent directory following the VFS locking protocol.
  */
 int
 fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
 {
-	int	nfserr = 0;
-
 	fh_lock(parent_fh);
 	/*
 	 * Make sure the parent->child relationship still holds,
 	 * and that the child is still hashed.
 	 */
-	if (dchild->d_parent != parent_fh->fh_dentry) 
-		goto out_not_parent;
-	if (list_empty(&dchild->d_hash))
-		goto out_not_hashed; 
-out:
-	return nfserr;
+	if (nfsd_check_parent(parent_fh->fh_dentry, dchild))
+	    return 0;
 
-out_not_parent:
 	printk(KERN_WARNING
-		"fh_lock_parent: %s/%s parent changed\n",
+		"fh_lock_parent: %s/%s parent changed or child unhashed\n",
 		dchild->d_parent->d_name.name, dchild->d_name.name);
-	goto out_unlock;
-out_not_hashed:
-	printk(KERN_WARNING
-		"fh_lock_parent: %s/%s unhashed\n",
-		dchild->d_parent->d_name.name, dchild->d_name.name);
-out_unlock:
-	nfserr = nfserr_noent;
-	fh_unlock(parent_fh);
-	goto out;
-}
 
-/*
- * Deny access to certain file systems
- */
-static inline int
-fs_off_limits(struct super_block *sb)
-{
-	return !sb || sb->s_magic == NFS_SUPER_MAGIC
-	           || sb->s_magic == PROC_SUPER_MAGIC;
+	fh_unlock(parent_fh);
+	return nfserr_noent;
 }
 
-/*
- * Check whether directory is a mount point, but it is all right if
- * this is precisely the local mount point being exported.
- */
-static inline int
-nfsd_iscovered(struct dentry *dentry, struct svc_export *exp)
-{
-	return (dentry != dentry->d_covers &&
-		dentry != exp->ex_dentry);
-}
 
 /*
  * Look up one component of a pathname.
  * N.B. After this call _both_ fhp and resfh need an fh_put
+ *
+ * If the lookup would cross a mountpoint, and the mounted filesystem
+ * is exported to the client with NFSEXP_CROSSMNT, then the lookup is
+ * accepted as it stands and the mounted directory is
+ * returned. Otherwise the covered directory is returned.
+ * NOTE: this mountpoint crossing is not supported properly by all
+ *   clients and is explicitly disallowed for NFSv3
+ *      NeilBrown <neilb@cse.unsw.edu.au>
  */
 int
 nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
@@ -166,33 +162,62 @@
 	if (err)
 		goto out;
 #endif
-	err = nfserr_noent;
-	if (fs_off_limits(dparent->d_sb))
-		goto out;
 	err = nfserr_acces;
-	if (nfsd_iscovered(dparent, exp))
-		goto out;
 
 	/* Lookup the name, but don't follow links */
-	dchild = lookup_dentry(name, dget(dparent), 0);
-	if (IS_ERR(dchild))
-		goto out_nfserr;
-	/*
-	 * check if we have crossed a mount point ...
-	 */
-	if (dchild->d_sb != dparent->d_sb) {
-		struct dentry *tdentry;
-		tdentry = dchild->d_covers;
-		if (tdentry == dchild)
-			goto out_dput;
-	        dput(dchild);
-		dchild = dget(tdentry);
-	        if (dchild->d_sb != dparent->d_sb) {
-printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, dchild->d_name.name);
-			goto out_dput;
+	if (strcmp(name, "..")==0) {
+		/* checking mountpoint crossing is very different when stepping up */
+		if (dparent == exp->ex_dentry) {
+			if (!EX_CROSSMNT(exp))
+				dchild = dget(dparent); /* .. == . just like at / */
+			else
+			{
+				struct svc_export *exp2 = NULL;
+				struct dentry *dp;
+				dchild = dparent->d_covers->d_parent;
+				for (dp=dchild;
+				     exp2 == NULL && dp->d_covers->d_parent != dp;
+				     dp=dp->d_covers->d_parent)
+					exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino);
+				if (exp2==NULL || dchild->d_sb != exp2->ex_dentry->d_sb) {
+					dchild = dget(dparent);
+				} else {
+					dget(dchild);
+					exp = exp2;
+				}
+			}
+		} else
+			dchild = dget(dparent->d_parent);
+	} else {
+		dchild = lookup_dentry(name, dget(dparent), 0);
+		if (IS_ERR(dchild))
+			goto out_nfserr;
+		/*
+		 * check if we have crossed a mount point ...
+		 */
+		if (dchild->d_sb != dparent->d_sb) {
+			struct svc_export *exp2 = NULL;
+			exp2 = exp_get(rqstp->rq_client,
+				       dchild->d_inode->i_dev,
+				       dchild->d_inode->i_ino);
+			if (exp2 && EX_CROSSMNT(exp2))
+				/* successfully crossed mount point */
+				exp = exp2;
+			else if (dchild->d_covers->d_sb == dparent->d_sb) {
+				/* stay in the original filesystem */
+				struct dentry *tdentry = dget(dchild->d_covers);
+				dput(dchild);
+				dchild = tdentry;
+			} else {
+				/* This cannot possibly happen */
+				printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
+				dput(dchild);
+				err = nfserr_acces;
+				goto out;
+
+			}
 		}
 	}
-
 	/*
 	 * Note: we compose the file handle now, but as the
 	 * dentry may be negative, it may need to be updated.
@@ -207,10 +232,6 @@
 out_nfserr:
 	err = nfserrno(-PTR_ERR(dchild));
 	goto out;
-out_dput:
-	dput(dchild);
-	err = nfserr_acces;
-	goto out;
 }
 
 /*
@@ -226,6 +247,8 @@
 	int		ftype = 0;
 	int		imode;
 	int		err;
+	kernel_cap_t	saved_cap = 0;
+	int		size_change = 0;
 
 	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
 		accmode |= MAY_WRITE;
@@ -234,39 +257,43 @@
 
 	/* Get inode */
 	err = fh_verify(rqstp, fhp, ftype, accmode);
-	if (err)
+	if (err || !iap->ia_valid)
 		goto out;
 
 	dentry = fhp->fh_dentry;
 	inode = dentry->d_inode;
 
 	err = inode_change_ok(inode, iap);
-	if (err) {
-		/* It is very tricky. When you are not the file owner,
-		   but have the write permission, you should be allowed
-		   to set atime and mtime to the current time on the
-		   server. However, the NFS V2 protocol doesn't support
-		   it. It has been fixed in V3. Here we do this: if the
-		   current server time and atime/mtime are close enough,
-		   we use the current server time. */
-#define CURRENT_TIME_SET	(ATTR_ATIME_SET | ATTR_MTIME_SET)
-		if (iap->ia_mtime == iap->ia_atime
-		    && ((iap->ia_valid & (CURRENT_TIME_SET))
-			== CURRENT_TIME_SET)) {
-			time_t now = CURRENT_TIME;
-			time_t delta = iap->ia_atime - now;
-			if (delta < 0) delta = -delta;
-			if (delta <= nfsd_time_diff_margin) {
-				iap->ia_valid &= ~CURRENT_TIME_SET;
-				goto current_time_ok;
-			}
+	/* could be a "touch" (utimes) request where the user is not the owner but does
+	 * have write permission. In this case the user should be allowed to set
+	 * both times to the current time.  We could just assume any such SETATTR
+	 * is intended to set the times to "now", but we do a couple of simple tests
+	 * to increase our confidence.
+	 */
+#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
+#define	MAX_TOUCH_TIME_ERROR (30*60)
+	if (err
+	    && (iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET
+	    && iap->ia_mtime == iap->ia_ctime
+	    ) {
+	    /* looks good.  now just make sure time is in the right ballpark.
+	     * solaris, at least, doesn't seem to care what the time request is
+	     */
+	    time_t delta = iap->ia_atime - CURRENT_TIME;
+	    if (delta<0) delta = -delta;
+	    if (delta < MAX_TOUCH_TIME_ERROR) {
+		/* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME
+		 * this will cause notify_change to setthese times to "now"
+		 */
+		iap->ia_valid &= ~BOTH_TIME_SET;
+		err = inode_change_ok(inode, iap);
 		}
-		goto out_nfserr;
 	}
 
-current_time_ok:
+	if (err)
+		goto out_nfserr;
 
-	/* The size case is special... */
+	/* The size case is special. It changes the file as well as the attributes.  */
 	if (iap->ia_valid & ATTR_SIZE) {
 if (!S_ISREG(inode->i_mode))
 printk("nfsd_setattr: size change??\n");
@@ -275,22 +302,12 @@
 			if (err)
 				goto out;
 		}
-		DQUOT_INIT(inode);
 		err = get_write_access(inode);
-		if (err) {
-			DQUOT_DROP(inode);
+		if (err)
 			goto out_nfserr;
-		}
-		/* N.B. Should we update the inode cache here? */
-		inode->i_size = iap->ia_size;
-		if (inode->i_op && inode->i_op->truncate)
-			inode->i_op->truncate(inode);
-		mark_inode_dirty(inode);
-		put_write_access(inode);
-		DQUOT_DROP(inode);
-		iap->ia_valid &= ~ATTR_SIZE;
-		iap->ia_valid |= ATTR_MTIME;
-		iap->ia_mtime = CURRENT_TIME;
+		size_change = 1;
+
+		DQUOT_INIT(inode);
 	}
 
 	imode = inode->i_mode;
@@ -312,24 +329,53 @@
 	}
 
 	/* Change the attributes. */
-	if (iap->ia_valid) {
-		kernel_cap_t	saved_cap = 0;
 
-		iap->ia_valid |= ATTR_CTIME;
-		iap->ia_ctime = CURRENT_TIME;
-		if (current->fsuid != 0) {
-			saved_cap = current->cap_effective;
-			cap_clear(current->cap_effective);
-		}
-		err = notify_change(dentry, iap);
-		if (current->fsuid != 0)
-			current->cap_effective = saved_cap;
-		if (err)
-			goto out_nfserr;
-		if (EX_ISSYNC(fhp->fh_export))
-			write_inode_now(inode);
+
+	iap->ia_valid |= ATTR_CTIME;
+	if (current->fsuid != 0) {
+		saved_cap = current->cap_effective;
+		cap_clear(current->cap_effective);
+	}
+#ifdef CONFIG_QUOTA
+	/* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */
+	if (iap->ia_valid & (ATTR_UID|ATTR_GID)) {
+		if (! (iap->ia_valid & ATTR_UID))
+			iap->ia_uid = inode->i_uid;
+		if (! (iap->ia_valid & ATTR_GID))
+			iap->ia_gid = inode->i_gid;
+		iap->ia_valid |= ATTR_UID|ATTR_GID;
 	}
+#endif /* CONFIG_QUOTA */
+
+	fh_lock(fhp);
+#ifdef CONFIG_QUOTA
+	if (iap->ia_valid & (ATTR_UID|ATTR_GID)) 
+		err = DQUOT_TRANSFER(dentry, iap);
+	else
+#endif
+		err = notify_change(dentry, iap);
+
+	if (size_change) {
+		if (!err) {
+			vmtruncate(inode,iap->ia_size);		
+			if (inode->i_op && inode->i_op->truncate)
+				inode->i_op->truncate(inode);
+		}
+		fh_unlock(fhp);
+		put_write_access(inode);
+	} else
+		fh_unlock(fhp);
+
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
+	if (err)
+		goto out_nfserr;
+	if (EX_ISSYNC(fhp->fh_export))
+		write_inode_now(inode);
 	err = 0;
+
+	/* Don't unlock inode; the nfssvc_release functions are supposed
+	 * to do this. */
 out:
 	return err;
 
@@ -338,20 +384,107 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * Check server access rights to a file system object
+ */
+struct accessmap {
+	u32		access;
+	int		how;
+};
+static struct accessmap	nfs3_regaccess[] = {
+    {	NFS3_ACCESS_READ,	MAY_READ			},
+    {	NFS3_ACCESS_EXECUTE,	MAY_EXEC			},
+    {	NFS3_ACCESS_MODIFY,	MAY_WRITE|MAY_TRUNC		},
+    {	NFS3_ACCESS_EXTEND,	MAY_WRITE			},
+
+    {	0,			0				}
+};
+
+static struct accessmap	nfs3_diraccess[] = {
+    {	NFS3_ACCESS_READ,	MAY_READ			},
+    {	NFS3_ACCESS_LOOKUP,	MAY_EXEC			},
+    {	NFS3_ACCESS_MODIFY,	MAY_EXEC|MAY_WRITE|MAY_TRUNC	},
+    {	NFS3_ACCESS_EXTEND,	MAY_EXEC|MAY_WRITE		},
+    {	NFS3_ACCESS_DELETE,	MAY_REMOVE			},
+
+    {	0,			0				}
+};
+
+static struct accessmap	nfs3_anyaccess[] = {
+    /* XXX: should we try to cover read/write here for clients that
+     * rely on us to do their access checking for special files? */
+
+    {	0,			0				}
+};
+
+int
+nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access)
+{
+	struct accessmap	*map;
+	struct svc_export	*export;
+	struct dentry		*dentry;
+	u32			query, result = 0;
+	int			error;
+
+	error = fh_verify(rqstp, fhp, 0, MAY_NOP);
+	if (error)
+		goto out;
+
+	export = fhp->fh_export;
+	dentry = fhp->fh_dentry;
+
+	if (S_ISREG(dentry->d_inode->i_mode)) {
+		map = nfs3_regaccess;
+	} else if (S_ISDIR(dentry->d_inode->i_mode)) {
+		map = nfs3_diraccess;
+	} else {
+		map = nfs3_anyaccess;
+	}
+
+	query = *access;
+	while (map->access) {
+		if (map->access & query) {
+			error = nfsd_permission(export, dentry, (map->how | NO_OWNER_OVERRIDE));
+			if (error == 0)
+				result |= map->access;
+			else if ((error == nfserr_perm) || (error == nfserr_acces)) {
+				/*
+				 *  This access type is denyed; but the 
+				 *  access query itself succeeds.
+				 */
+				error = 0;
+			} else {
+				/*
+				 *  Some fatal error.  Fail the query.
+				 */
+				goto out;
+			}
+		}
+		map++;
+	}
+	*access = result;
+
+out:
+	return error;
+}
+#endif
+
+
+
 /*
  * Open an existing file or directory.
- * The wflag argument indicates write access.
+ * The access argument indicates the type of open (read/write/lock)
  * N.B. After this call fhp needs an fh_put
  */
 int
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
-			int wflag, struct file *filp)
+			int access, struct file *filp)
 {
 	struct dentry	*dentry;
 	struct inode	*inode;
-	int		access, err;
+	int		err;
 
-	access = wflag? MAY_WRITE : MAY_READ;
 	err = fh_verify(rqstp, fhp, type, access);
 	if (err)
 		goto out;
@@ -368,24 +501,27 @@
 	if (!inode->i_op || !inode->i_op->default_file_ops)
 		goto out;
 
-	if (wflag && (err = get_write_access(inode)) != 0)
+	if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0)
 		goto out_nfserr;
 
 	memset(filp, 0, sizeof(*filp));
 	filp->f_op    = inode->i_op->default_file_ops;
 	filp->f_count = 1;
-	filp->f_flags = wflag? O_WRONLY : O_RDONLY;
-	filp->f_mode  = wflag? FMODE_WRITE : FMODE_READ;
 	filp->f_dentry = dentry;
-
-	if (wflag)
+	if (access & MAY_WRITE) {
+		filp->f_flags = O_WRONLY;
+		filp->f_mode  = FMODE_WRITE;
 		DQUOT_INIT(inode);
+	} else {
+		filp->f_flags = O_RDONLY;
+		filp->f_mode  = FMODE_READ;
+	}
 
 	err = 0;
 	if (filp->f_op && filp->f_op->open) {
 		err = filp->f_op->open(inode, filp);
 		if (err) {
-			if (wflag)
+			if (access & MAY_WRITE)
 				put_write_access(inode);
 
 			/* I nearly added put_filp() call here, but this filp
@@ -419,17 +555,33 @@
 		filp->f_op->release(inode, filp);
 	if (filp->f_mode & FMODE_WRITE) {
 		put_write_access(inode);
-		DQUOT_DROP(inode);
 	}
 }
 
 /*
  * Sync a file
+ * As this calls fsync (not fdatasync) there is no need for a write_inode
+ * after it.
  */
 void
-nfsd_sync(struct inode *inode, struct file *filp)
+nfsd_sync(struct file *filp)
 {
+	dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name);
+	down(&filp->f_dentry->d_inode->i_sem);
 	filp->f_op->fsync(filp, filp->f_dentry);
+	up(&filp->f_dentry->d_inode->i_sem);
+}
+
+void
+nfsd_sync_dir(struct dentry *dp)
+{
+	struct inode *inode = dp->d_inode;
+	int (*fsync) (struct file *, struct dentry *);
+	
+	if (inode->i_op->default_file_ops
+	    && (fsync = inode->i_op->default_file_ops->fsync)) {
+		fsync(NULL, dp);
+	}
 }
 
 /*
@@ -478,7 +630,7 @@
 	int		err;
 	struct file	file;
 
-	err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file);
+	err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file);
 	if (err)
 		goto out;
 	err = nfserr_perm;
@@ -543,11 +695,11 @@
 	uid_t			saved_euid;
 #endif
 
-	if (!cnt)
-		goto out;
-	err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file);
+	err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file);
 	if (err)
 		goto out;
+	if (!cnt)
+		goto out_close;
 	err = nfserr_perm;
 	if (!file.f_op->write)
 		goto out_close;
@@ -560,11 +712,21 @@
 	 * Request sync writes if
 	 *  -	the sync export option has been set, or
 	 *  -	the client requested O_SYNC behavior (NFSv3 feature).
+	 *  -   The file system doesn't support fsync().
 	 * When gathered writes have been configured for this volume,
 	 * flushing the data to disk is handled separately below.
 	 */
+#ifdef CONFIG_NFSD_V3
+	if (rqstp->rq_vers == 2)
+		stable = EX_ISSYNC(exp);
+	else if (file.f_op->fsync == 0)
+	       stable = 1;
+	if (stable && !EX_WGATHER(exp))
+		file.f_flags |= O_SYNC;
+#else
 	if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp))
 		file.f_flags |= O_SYNC;
+#endif /* CONFIG_NFSD_V3 */
 
 	fh_lock(fhp);			/* lock inode */
 	file.f_pos = offset;		/* set write offset */
@@ -618,26 +780,25 @@
 		 */
 		if (EX_WGATHER(exp) && (inode->i_writecount > 1
 		 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) {
-#if 0
-			interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000);
-#else
 			dprintk("nfsd: write defer %d\n", current->pid);
+			current->state = TASK_UNINTERRUPTIBLE;
 			schedule_timeout((HZ+99)/100);
+			current->state = TASK_RUNNING;
 			dprintk("nfsd: write resume %d\n", current->pid);
-#endif
 		}
 
 		if (inode->i_state & I_DIRTY) {
 			dprintk("nfsd: write sync %d\n", current->pid);
-			nfsd_sync(inode, &file);
-			write_inode_now(inode);
+			nfsd_sync(&file);
 		}
+#if 0
 		wake_up(&inode->i_wait);
+#endif
 		last_ino = inode->i_ino;
 		last_dev = inode->i_dev;
 	}
 
-	dprintk("nfsd: write complete\n");
+	dprintk("nfsd: write complete err=%d\n", err);
 	if (err >= 0)
 		err = 0;
 	else 
@@ -648,6 +809,38 @@
 	return err;
 }
 
+
+#ifdef CONFIG_NFSD_V3
+/*
+ * Commit all pendig writes to stable storage.
+ * Strictly speaking, we could sync just indicated the file region here,
+ * but there's currently no way we can ask the VFS to do so.
+ *
+ * We lock the file to make sure we return full WCC data to the client.
+ */
+int
+nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               off_t offset, unsigned long count)
+{
+	struct file	file;
+	int		err;
+
+	if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
+		return err;
+
+	fh_lock(fhp);
+	if (file.f_op && file.f_op->fsync) {
+		file.f_op->fsync(&file, file.f_dentry);
+	} else {
+		err = nfserr_notsupp;
+	}
+	fh_unlock(fhp);
+
+	nfsd_close(&file);
+	return err;
+}
+#endif /* CONFIG_NFSD_V3 */
+
 /*
  * Create a file (regular, directory, device, fifo); UNIX sockets 
  * not yet implemented.
@@ -669,6 +862,7 @@
 	err = nfserr_perm;
 	if (!flen)
 		goto out;
+
 	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
 	if (err)
 		goto out;
@@ -679,40 +873,42 @@
 	err = nfserr_notdir;
 	if(!dirp->i_op || !dirp->i_op->lookup)
 		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
+		goto out;
 	/*
 	 * Check whether the response file handle has been verified yet.
 	 * If it has, the parent directory should already be locked.
 	 */
-	if (!resfhp->fh_dverified) {
+	if (!resfhp->fh_dentry) {
 		dchild = lookup_dentry(fname, dget(dentry), 0);
 		err = PTR_ERR(dchild);
 		if (IS_ERR(dchild))
 			goto out_nfserr;
 		fh_compose(resfhp, fhp->fh_export, dchild);
+		/* Lock the parent and check for errors ... */
+		err = fh_lock_parent(fhp, dchild);
+		if (err)
+			goto out;
 	} else {
 		dchild = resfhp->fh_dentry;
-		if (!fhp->fh_locked)
+		if (!fhp->fh_locked) {
+			/* not actually possible */
 			printk(KERN_ERR
 				"nfsd_create: parent %s/%s not locked!\n",
 				dentry->d_parent->d_name.name,
 				dentry->d_name.name);
-	}
-	err = nfserr_exist;
-	if (dchild->d_inode)
-		goto out;
-	if (!fhp->fh_locked) {
-		/* Lock the parent and check for errors ... */
-		err = fh_lock_parent(fhp, dchild);
-		if (err)
+			err = -EIO;
 			goto out;
 	}
+	}
 	/*
 	 * Make sure the child dentry is still negative ...
 	 */
 	err = nfserr_exist;
 	if (dchild->d_inode) {
-		printk(KERN_WARNING
-			"nfsd_create: dentry %s/%s not negative!\n",
+		dprintk("nfsd_create: dentry %s/%s not negative!\n",
 			dentry->d_name.name, dchild->d_name.name);
 		goto out; 
 	}
@@ -739,24 +935,29 @@
 	case S_IFSOCK:
 		opfunc = dirp->i_op->mknod;
 		break;
+	default:
+	        printk("nfsd: bad file type %o in nfsd_create\n", type);
+		err = nfserr_inval;
 	}
 	if (!opfunc)
 		goto out;
 
 	if (!(iap->ia_valid & ATTR_MODE))
 		iap->ia_mode = 0;
+	iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type;
 
 	/*
 	 * Call the dir op function to create the object.
 	 */
 	DQUOT_INIT(dirp);
 	err = opfunc(dirp, dchild, iap->ia_mode, rdev);
-	DQUOT_DROP(dirp);
 	if (err < 0)
 		goto out_nfserr;
 
-	if (EX_ISSYNC(fhp->fh_export))
-		write_inode_now(dirp);
+	if (EX_ISSYNC(fhp->fh_export)) {
+		nfsd_sync_dir(dentry);
+		write_inode_now(dchild->d_inode);
+	}
 
 	/*
 	 * Update the file handle to get the new inode info.
@@ -779,6 +980,129 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * NFSv3 version of nfsd_create
+ */
+int
+nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		char *fname, int flen, struct iattr *iap,
+		struct svc_fh *resfhp, int createmode, u32 *verifier)
+{
+	struct dentry	*dentry, *dchild;
+	struct inode	*dirp;
+	int		err;
+
+	err = nfserr_perm;
+	if (!flen)
+		goto out;
+	if (!(iap->ia_valid & ATTR_MODE))
+		iap->ia_mode = 0;
+	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+	if (err)
+		goto out;
+
+	dentry = fhp->fh_dentry;
+	dirp = dentry->d_inode;
+
+	/* Get all the sanity checks out of the way before
+	 * we lock the parent. */
+	err = nfserr_notdir;
+	if(!dirp->i_op || !dirp->i_op->lookup)
+		goto out;
+	err = nfserr_perm;
+	if(!dirp->i_op->create)
+		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
+		goto out;
+	/*
+	 * Compose the response file handle.
+	 */
+	dchild = lookup_dentry(fname, dget(dentry), 0);
+	err = PTR_ERR(dchild);
+	if(IS_ERR(dchild))
+		goto out_nfserr;
+	fh_compose(resfhp, fhp->fh_export, dchild);
+
+	/*
+	 * We must lock the directory before we check for the inode.
+	 */
+	err = fh_lock_parent(fhp, dchild);
+	if (err)
+	    goto out;
+
+	if (dchild->d_inode) {
+		err = 0;
+
+		if (resfhp->fh_handle.fh_ino == 0)
+		     /* inode might have been instantiated while we slept */
+		    fh_update(resfhp);
+		
+		switch (createmode) {
+		case NFS3_CREATE_UNCHECKED:
+			if (! S_ISREG(dchild->d_inode->i_mode))
+				err = nfserr_exist;
+			else {
+				iap->ia_valid &= ATTR_SIZE;
+				goto set_attr;
+			}
+			break;
+		case NFS3_CREATE_EXCLUSIVE:
+			if (   dchild->d_inode->i_mtime == verifier[0]
+			    && dchild->d_inode->i_atime == verifier[1]
+			    && dchild->d_inode->i_mode == S_IFREG
+			    && dchild->d_inode->i_size == 0 )
+				break;
+			 /* fallthru */
+		case NFS3_CREATE_GUARDED:
+			err = nfserr_exist;
+		}
+		goto out;
+	}
+
+	err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
+	if (err < 0)
+		goto out_nfserr;
+
+	if (EX_ISSYNC(fhp->fh_export)) {
+		nfsd_sync_dir(dentry);
+		/* setattr will sync the child (or not) */
+	}
+
+	/*
+	 * Update the filehandle to get the new inode info.
+	 */
+	fh_update(resfhp);
+	err = 0;
+
+	if (createmode == NFS3_CREATE_EXCLUSIVE) {
+		/* Cram the verifier into atime/mtime */
+		iap->ia_valid = ATTR_MTIME|ATTR_ATIME|ATTR_MTIME_SET|ATTR_ATIME_SET;
+		iap->ia_mtime = verifier[0];
+		iap->ia_atime = verifier[1];
+	}
+
+	/* Set file attributes. Mode has already been set and
+	 * setting uid/gid works only for root. Irix appears to
+	 * send along the gid when it tries to implement setgid
+	 * directories via NFS. Clear out all that cruft.
+	 */
+ set_attr:
+	if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+ 		err = nfsd_setattr(rqstp, resfhp, iap);
+
+ out:
+	fh_unlock(fhp);
+ 	return err;
+ 
+ out_nfserr:
+	err = nfserrno(-err);
+	goto out;
+}
+#endif /* CONFIG_NFSD_V3 */
+
 /*
  * Truncate a file.
  * The calling routines must make sure to update the ctime
@@ -817,15 +1141,16 @@
 		cap_clear(current->cap_effective);
 	}
 	err = notify_change(dentry, &newattrs);
-	if (current->fsuid != 0)
-		current->cap_effective = saved_cap;
 	if (!err) {
 		vmtruncate(inode, size);
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
 	}
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
 	put_write_access(inode);
-	DQUOT_DROP(inode);
+	if (EX_ISSYNC(fhp->fh_export))
+		nfsd_sync_dir(dentry);
 	fh_unlock(fhp);
 out_nfserr:
 	if (err)
@@ -859,7 +1184,10 @@
 		goto out;
 
 	UPDATE_ATIME(inode);
-	/* N.B. Why does this call need a get_fs()?? */
+	/* N.B. Why does this call need a get_fs()??
+	 * Remove the set_fs and watch the fireworks:-) --okir
+	 */
+
 	oldfs = get_fs(); set_fs(KERNEL_DS);
 	err = inode->i_op->readlink(dentry, buf, *lenp);
 	set_fs(oldfs);
@@ -884,7 +1212,8 @@
 nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
 				char *fname, int flen,
 				char *path,  int plen,
-				struct svc_fh *resfhp)
+				struct svc_fh *resfhp,
+				struct iattr *iap)
 {
 	struct dentry	*dentry, *dnew;
 	struct inode	*dirp;
@@ -899,9 +1228,11 @@
 		goto out;
 	dentry = fhp->fh_dentry;
 
-	err = nfserr_perm;
-	if (nfsd_iscovered(dentry, fhp->fh_export))
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
 		goto out;
+
+	err = nfserr_perm;
 	dirp = dentry->d_inode;
 	if (!dirp->i_op	|| !dirp->i_op->symlink)
 		goto out;
@@ -922,10 +1253,20 @@
 	if (!dnew->d_inode) {
 		DQUOT_INIT(dirp);
 		err = dirp->i_op->symlink(dirp, dnew, path);
-		DQUOT_DROP(dirp);
 		if (!err) {
 			if (EX_ISSYNC(fhp->fh_export))
-				write_inode_now(dirp);
+				nfsd_sync_dir(dentry);
+			if (iap) {
+				iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/;
+				if (iap->ia_valid) {
+					iap->ia_valid |= ATTR_CTIME;
+					iap->ia_mode = (iap->ia_mode&S_IALLUGO)
+						| S_IFLNK;
+					err = notify_change(dnew, iap);
+					if (!err && EX_ISSYNC(fhp->fh_export))
+						write_inode_now(dentry->d_inode);
+			       }
+			}
 		} else
 			err = nfserrno(-err);
 	}
@@ -949,7 +1290,7 @@
  */
 int
 nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
-				char *fname, int len, struct svc_fh *tfhp)
+				char *fname, int flen, struct svc_fh *tfhp)
 {
 	struct dentry	*ddir, *dnew, *dold;
 	struct inode	*dirp, *dest;
@@ -958,12 +1299,16 @@
 	err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE);
 	if (err)
 		goto out;
-	err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP);
+	err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP);
 	if (err)
 		goto out;
 
 	err = nfserr_perm;
-	if (!len)
+	if (!flen)
+		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
 		goto out;
 
 	ddir = ffhp->fh_dentry;
@@ -987,10 +1332,7 @@
 	dold = tfhp->fh_dentry;
 	dest = dold->d_inode;
 
-	err = nfserr_acces;
-	if (nfsd_iscovered(ddir, ffhp->fh_export))
-		goto out_unlock;
-	/* FIXME: nxdev for NFSv3 */
+	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
 	if (dirp->i_dev != dest->i_dev)
 		goto out_unlock;
 
@@ -1002,10 +1344,9 @@
 
 	DQUOT_INIT(dirp);
 	err = dirp->i_op->link(dold, dirp, dnew);
-	DQUOT_DROP(dirp);
 	if (!err) {
 		if (EX_ISSYNC(ffhp->fh_export)) {
-			write_inode_now(dirp);
+			nfsd_sync_dir(ddir);
 			write_inode_now(dest);
 		}
 	} else
@@ -1024,26 +1365,12 @@
 }
 
 /*
- * We need to do a check-parent every time
- * after we have locked the parent - to verify
- * that the parent is still our parent and
- * that we are still hashed onto it..
- *
- * This is requied in case two processes race
- * on removing (or moving) the same entry: the
- * parent lock will serialize them, but the
- * other process will be too late..
- */
-#define check_parent(dir, dentry) \
-	((dir) == (dentry)->d_parent->d_inode && !list_empty(&dentry->d_hash))
-
-/*
  * This follows the model of double_lock() in the VFS.
  */
 static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
 {
 	if (s1 != s2) {
-		if ((unsigned long) s1 > (unsigned long) s2) {
+		if ((unsigned long) s1 < (unsigned long) s2) {
 			struct semaphore *tmp = s1;
 			s1 = s2;
 			s2 = tmp;
@@ -1085,12 +1412,12 @@
 	tdentry = tfhp->fh_dentry;
 	tdir = tdentry->d_inode;
 
-	/* N.B. We shouldn't need this ... dentry layer handles it */
+	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
+	if (fdir->i_dev != tdir->i_dev)
+		goto out;
+
 	err = nfserr_perm;
-	if (!flen || (fname[0] == '.' && 
-	    (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
-	    !tlen || (tname[0] == '.' && 
-	    (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
+	if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
 		goto out;
 
 	odentry = lookup_dentry(fname, dget(fdentry), 0);
@@ -1111,31 +1438,36 @@
 	 * Lock the parent directories.
 	 */
 	nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
+
+#ifdef CONFIG_NFSD_V3
+	/* Fill in the pre-op attr for the wcc data for both 
+	 * tdir and fdir
+	 */ 
+	fill_pre_wcc(ffhp);
+	fill_pre_wcc(tfhp);
+#endif /* CONFIG_NFSD_V3 */
+
 	err = -ENOENT;
 	/* GAM3 check for parent changes after locking. */
-	if (check_parent(fdir, odentry) &&
-	    check_parent(tdir, ndentry)) {
+	if (nfsd_check_parent(fdentry, odentry) &&
+	    nfsd_check_parent(tdentry, ndentry)) {
 
 		err = vfs_rename(fdir, odentry, tdir, ndentry);
 		if (!err && EX_ISSYNC(tfhp->fh_export)) {
-			write_inode_now(fdir);
-			write_inode_now(tdir);
+			nfsd_sync_dir(tdentry);
+			nfsd_sync_dir(fdentry);
 		}
 	} else
 		dprintk("nfsd: Caught race in nfsd_rename");
-	DQUOT_DROP(fdir);
-	DQUOT_DROP(tdir);
 
+#ifdef CONFIG_NFSD_V3
+        /* Fill in the post-op attr for the wcc data for both 
+         * tdir and fdir
+         */
+	fill_post_wcc(ffhp);
+	fill_post_wcc(tfhp);
+#endif /* CONFIG_NFSD_V3 */
 	nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
-
-	if (!err && odentry->d_inode) {
-		add_to_rename_cache(tdir->i_ino,
-				    odentry->d_inode->i_dev,
-				    fdir->i_ino,
-				    odentry->d_inode->i_ino);
-	} else {
-		printk(": no inode in rename or err: %d.\n", err);
-	}
 	dput(ndentry);
 
 out_dput_old:
@@ -1162,13 +1494,12 @@
 	struct inode	*dirp;
 	int		err;
 
-	/* N.B. We shouldn't need this test ... handled by dentry layer */
-	err = nfserr_acces;
-	if (!flen || isdotent(fname, flen))
-		goto out;
 	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE);
 	if (err)
 		goto out;
+	err = nfserr_acces;
+	if (!flen || isdotent(fname, flen))
+		goto out;
 
 	dentry = fhp->fh_dentry;
 	dirp = dentry->d_inode;
@@ -1177,13 +1508,13 @@
 	err = PTR_ERR(rdentry);
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
+
 	if (!rdentry->d_inode) {
 		dput(rdentry);
 		err = nfserr_noent;
 		goto out;
 	}
 
-	expire_by_dentry(rdentry);
 
 	if (type != S_IFDIR) {
 		/* It's UNLINK */
@@ -1194,30 +1525,33 @@
 
 		err = vfs_unlink(dirp, rdentry);
 
-		DQUOT_DROP(dirp);
 		fh_unlock(fhp);
 
 		dput(rdentry);
-		expire_by_dentry(rdentry);
+
 	} else {
 		/* It's RMDIR */
 		/* See comments in fs/namei.c:do_rmdir */
 
 		rdentry->d_count++;
 		nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem);
-		if (!fhp->fh_pre_mtime)
-			fhp->fh_pre_mtime = dirp->i_mtime;
+
+#ifdef CONFIG_NFSD_V3
+		fill_pre_wcc(fhp);
+#else
 		fhp->fh_locked = 1;
+#endif /* CONFIG_NFSD_V3 */
 
 		err = -ENOENT;
-		if (check_parent(dirp, rdentry))
+		if (nfsd_check_parent(dentry, rdentry))
 			err = vfs_rmdir(dirp, rdentry);
 
 		rdentry->d_count--;
-		DQUOT_DROP(dirp);
-		if (!fhp->fh_post_version)
-			fhp->fh_post_version = dirp->i_version;
+#ifdef CONFIG_NFSD_V3
+		fill_post_wcc(fhp);
+#else
 		fhp->fh_locked = 0;
+#endif /* CONFIG_NFSD_V3 */
 		nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem);
 
 		dput(rdentry);
@@ -1225,9 +1559,11 @@
 
 	if (err)
 		goto out_nfserr;
-	if (EX_ISSYNC(fhp->fh_export))
-		write_inode_now(dirp);
-
+	if (EX_ISSYNC(fhp->fh_export)) {
+		down(&dentry->d_inode->i_sem);
+		nfsd_sync_dir(dentry);
+		up(&dentry->d_inode->i_sem);
+	}
 out:
 	return err;
 
@@ -1238,10 +1574,11 @@
 
 /*
  * Read entries from a directory.
+ * The verifier is an NFSv3 thing we ignore for now.
  */
 int
 nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, 
-			encode_dent_fn func, u32 *buffer, int *countp)
+             encode_dent_fn func, u32 *buffer, int *countp, u32 *verf)
 {
 	struct inode	*inode;
 	u32		*p;
@@ -1249,13 +1586,11 @@
 	struct file	file;
 	struct readdir_cd cd;
 
-	err = 0;
-	if (offset > ~(u32) 0)
-		goto out;
-
-	err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file);
+	err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file);
 	if (err)
 		goto out;
+	if (offset > ~(u32) 0)
+		goto out_close;
 
 	err = nfserr_notdir;
 	if (!file.f_op->readdir)
@@ -1267,6 +1602,7 @@
 	cd.rqstp  = rqstp;
 	cd.buffer = buffer;
 	cd.buflen = *countp; /* count of words */
+	cd.dirfh  = fhp;
 
 	/*
 	 * Read the directory entries. This silly loop is necessary because
@@ -1296,8 +1632,14 @@
 	/* If we didn't fill the buffer completely, we're at EOF */
 	eof = !cd.eob;
 
-	if (cd.offset)
-		*cd.offset = htonl(file.f_pos);
+	if (cd.offset) {
+#ifdef CONFIG_NFSD_V3
+		if (rqstp->rq_vers == 3)
+			(void)enc64(cd.offset, file.f_pos);
+		else
+#endif /* CONFIG_NFSD_V3 */
+			*cd.offset = htonl(file.f_pos);
+	}
 
 	p = cd.buffer;
 	*p++ = 0;			/* no more entries */
@@ -1360,17 +1702,33 @@
 	struct inode	*inode = dentry->d_inode;
 	int		err;
 	kernel_cap_t	saved_cap = 0;
+	int		owneraccess;
+
+	/*
+	 *  Check if we are to use "owner may always access" semantics,
+	 *  then clean out the flag bit which controls this. It might be
+	 *  clearer to reverse the logic of this flag, but I didn't
+	 *  want to change a lot of code in a stable kernel - dhiggen.
+	 */
+
+	if (acc & NO_OWNER_OVERRIDE) {
+		owneraccess = 0;
+		acc &= ~NO_OWNER_OVERRIDE;
+	} else {
+		owneraccess = 1;
+	}
 
 	if (acc == MAY_NOP)
 		return 0;
 #if 0
-	dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n",
+	dprintk("nfsd: permission 0x%x%s%s%s%s%s%s mode 0%o%s%s%s\n",
 		acc,
 		(acc & MAY_READ)?	" read"  : "",
 		(acc & MAY_WRITE)?	" write" : "",
 		(acc & MAY_EXEC)?	" exec"  : "",
 		(acc & MAY_SATTR)?	" sattr" : "",
 		(acc & MAY_TRUNC)?	" trunc" : "",
+		(acc & MAY_LOCK)?	" lock"  : "",
 		inode->i_mode,
 		IS_IMMUTABLE(inode)?	" immut" : "",
 		IS_APPEND(inode)?	" append" : "",
@@ -1378,33 +1736,40 @@
 	dprintk("      owner %d/%d user %d/%d\n",
 		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
 #endif
-#ifndef CONFIG_NFSD_SUN
-	if (dentry->d_mounts != dentry) {
-		return nfserr_perm;
-	}
-#endif
 
 	if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
 		if (EX_RDONLY(exp) || IS_RDONLY(inode))
 			return nfserr_rofs;
-		if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp))
-			return nfserr_perm;
 		if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
 			return nfserr_perm;
 	}
 	if ((acc & MAY_TRUNC) && IS_APPEND(inode))
 		return nfserr_perm;
 
+	if (acc & MAY_LOCK) {
+		/* If we cannot rely on authentication in NLM requests,
+		 * just allow locks, others require read permission
+		 */
+		if (exp->ex_flags & NFSEXP_NOAUTHNLM)
+			return 0;
+		else
+			acc = MAY_READ;
+	}
 	/*
-	 * The file owner always gets access permission. This is to make
-	 * file access work even when the client has done a fchmod(fd, 0).
+	 * The file owner always gets access permission (except in the 
+	 * special case of a V3 ACCESS call, which is used for checking at 
+	 * open() time). This is to make file access work even when the 
+	 * client has done a fchmod(fd, 0).
 	 *
 	 * However, `cp foo bar' should fail nevertheless when bar is
 	 * readonly. A sensible way to do this might be to reject all
 	 * attempts to truncate a read-only file, because a creat() call
 	 * always implies file truncation.
+	 *  dhXXX we are not currently setting MAY_TRUNC from a SETATTR which
+	 *  changes the size, so this check is not enforced.  It probably
+	 *  should be?
 	 */
-	if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
+	if (owneraccess && inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
 		return 0;
 
 	if (current->fsuid != 0) {
diff -urN linux-2.2.18-pre15/fs/super.c linux/fs/super.c
--- linux-2.2.18-pre15/fs/super.c	Mon Dec  3 20:33:36 2001
+++ linux/fs/super.c	Mon Dec  3 20:34:12 2001
@@ -566,6 +566,7 @@
 	s->s_flags = flags;
 	s->s_dirt = 0;
 	sema_init(&s->s_vfs_rename_sem,1);
+	sema_init(&s->s_nfsd_free_path_sem,1);
 	/* N.B. Should lock superblock now ... */
 	if (!type->read_super(s, data, silent))
 		goto out_fail;
@@ -1154,6 +1155,7 @@
 			sb->s_dev = get_unnamed_dev();
 			sb->s_flags = root_mountflags;
 			sema_init(&sb->s_vfs_rename_sem,1);
+			sema_init(&sb->s_nfsd_free_path_sem,1);
 			vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
 			if (vfsmnt) {
 				if (nfs_root_mount(sb) >= 0) {
diff -urN linux-2.2.18-pre15/include/asm-i386/adb_iop.h linux/include/asm-i386/adb_iop.h
--- linux-2.2.18-pre15/include/asm-i386/adb_iop.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/adb_iop.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,44 @@
+/*
+ * ADB through the IOP
+ * Written by Joshua M. Thompson
+ */
+
+/* IOP number and channel number for ADB */
+
+#define ADB_IOP		IOP_NUM_ISM
+#define ADB_CHAN	2
+
+/* From the A/UX headers...maybe important, maybe not */
+
+#define ADB_IOP_LISTEN	0x01
+#define ADB_IOP_TALK	0x02
+#define ADB_IOP_EXISTS	0x04
+#define ADB_IOP_FLUSH	0x08
+#define ADB_IOP_RESET	0x10
+#define ADB_IOP_INT	0x20
+#define ADB_IOP_POLL	0x40
+#define ADB_IOP_UNINT	0x80
+
+#define AIF_RESET	0x00
+#define AIF_FLUSH	0x01
+#define AIF_LISTEN	0x08
+#define AIF_TALK	0x0C
+
+/* Flag bits in struct adb_iopmsg */
+
+#define ADB_IOP_EXPLICIT	0x80	/* nonzero if explicit command */
+#define ADB_IOP_AUTOPOLL	0x40	/* auto/SRQ polling enabled    */
+#define ADB_IOP_SRQ		0x04	/* SRQ detected                */
+#define ADB_IOP_TIMEOUT		0x02	/* nonzero if timeout          */
+
+#ifndef __ASSEMBLY__
+
+struct adb_iopmsg {
+	__u8 flags;		/* ADB flags         */
+	__u8 count;		/* no. of data bytes */
+	__u8 cmd;		/* ADB command       */
+	__u8 data[8];		/* ADB data          */
+	__u8 spare[21];		/* spare             */
+};
+
+#endif /* __ASSEMBLY__ */
diff -urN linux-2.2.18-pre15/include/asm-i386/m68kserial.h linux/include/asm-i386/m68kserial.h
--- linux-2.2.18-pre15/include/asm-i386/m68kserial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/m68kserial.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,454 @@
+/*
+ * include/linux/m68kserial.h
+ *
+ * Copyright (C) 1992 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ *
+ * 2/28/99 RZ: renamed to allow coexistence with serial.c driver
+ */
+
+
+#ifndef _M68K_SERIAL_H
+#define _M68K_SERIAL_H
+
+
+/* m68k serial port types are numbered from 100 to avoid interference
+ * with the PC types (1..4)
+ */
+#define PORT_UNKNOWN	0
+#define PORT_8250	1
+#define PORT_16450	2
+#define PORT_16550	3
+#define PORT_16550A	4
+#define PORT_CIRRUS     5
+#define PORT_16650V2	7
+#define PORT_16750	8
+
+#define SER_SCC_NORM	100	/* standard SCC channel */
+#define	SER_SCC_DMA	101	/* SCC channel with DMA support */
+#define	SER_MFP_CTRL	102	/* standard MFP port with modem control signals */
+#define	SER_MFP_BARE	103	/* MFP port without modem controls */
+#define	SER_MIDI	104	/* Atari MIDI */
+#define	SER_AMIGA	105	/* Amiga built-in serial port */
+#define SER_IOEXT	106	/* Amiga GVP IO-Extender (16c552) */
+#define SER_MFC_III	107	/* Amiga BSC Multiface Card III (MC68681) */
+#define SER_WHIPPET	108	/* Amiga Hisoft Whippet PCMCIA (16c550B) */
+#define SER_SCC_MVME	109	/* MVME162/MVME172 ports */
+#define SER_SCC_MAC	110	/* Macintosh SCC channel */
+#define SER_HPDCA	111	/* HP DCA serial */
+#define SER_SCC_BVME	112	/* BVME6000 ports */
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ASYNC_CLOSING_WAIT_INF	0
+#define ASYNC_CLOSING_WAIT_NONE	65535
+
+/* This function tables does the abstraction from the underlying
+ * hardware:
+ *
+ *   init(): Initialize the port as necessary, set RTS and DTR and
+ *      enable interrupts. It does not need to set the speed and other
+ *      parameters, because change_speed() is called, too.
+ *   deinit(): Stop and shutdown the port (e.g. disable interrupts, ...)
+ *   enab_tx_int(): Enable or disable the Tx Buffer Empty interrupt
+ *      independently from other interrupt sources. If the int is
+ *      enabled, the transmitter should also be restarted, i.e. if there
+ *      are any chars to be sent, they should be put into the Tx
+ *      register. The real en/disabling of the interrupt may be a no-op
+ *      if there is no way to do this or it is too complex. This Tx ints
+ *      are just disabled to save some interrupts if the transmitter is
+ *      stopped anyway. But the restarting must be implemented!
+ *   check_custom_divisor(): Check the given custom divisor for legality
+ *      and return 0 if OK, non-zero otherwise.
+ *   change_speed(): Set port speed, character size, number of stop
+ *      bits and parity from the termios structure. If the user wants
+ *      to set the speed with a custom divisor, he is required to
+ *      check the baud_base first!
+ *   throttle(): Set or clear the RTS line according to 'status'.
+ *   set_break(): Set or clear the 'Send a Break' flag.
+ *   get_serial_info(): Fill in the baud_base and custom_divisor
+ *      fields of a serial_struct. It may also modify other fields, if
+ *      needed.
+ *   get_modem_info(): Return the status of RTS, DTR, DCD, RI, DSR and CTS.
+ *   set_modem_info(): Set the status of RTS and DTR according to
+ *      'new_dtr' and 'new_rts', resp. 0 = clear, 1 = set, -1 = don't change
+ *   ioctl(): Process any port-specific ioctl's. This pointer may be
+ *      NULL, if the port has no own ioctl's.
+ *   stop_receive(): Turn off the Rx part of the port, so no more characters
+ *      will be received. This is called before shutting the port down.
+ *   trans_empty(): Return !=0 if there are no more characters still to be
+ *      sent out (Tx buffer register and FIFOs empty)
+ *   check_open(): Is called before the port is opened. The driver can check
+ *      if that's ok and return an error code, or keep track of the opening
+ *      even before init() is called. Use deinit() for matching closing of the
+ *      port.
+ *
+ */
+
+struct m68k_async_struct;
+
+typedef struct {
+	void (*init)( struct m68k_async_struct *info );
+	void (*deinit)( struct m68k_async_struct *info, int leave_dtr );
+	void (*enab_tx_int)( struct m68k_async_struct *info, int enab_flag );
+	int  (*check_custom_divisor)( struct m68k_async_struct *info, int baud_base,
+				     int divisor );
+	void (*change_speed)( struct m68k_async_struct *info );
+	void (*throttle)( struct m68k_async_struct *info, int status );
+	void (*set_break)( struct m68k_async_struct *info, int break_flag );
+	void (*get_serial_info)( struct m68k_async_struct *info,
+				struct serial_struct *retinfo );
+	unsigned int (*get_modem_info)( struct m68k_async_struct *info );
+	int  (*set_modem_info)( struct m68k_async_struct *info, int new_dtr,
+			       int new_rts );
+	int  (*ioctl)( struct tty_struct *tty, struct file *file,
+		      struct m68k_async_struct *info, unsigned int cmd,
+		      unsigned long arg );
+	void (*stop_receive)( struct m68k_async_struct *info );
+	int  (*trans_empty)( struct m68k_async_struct *info );
+	int  (*check_open)( struct m68k_async_struct *info, struct tty_struct *tty,
+			   struct file *file );
+} SERIALSWITCH;
+
+/*
+ * Definitions for m68k_async_struct (and serial_struct) flags field
+ */
+#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
+				   on the callout port */
+#define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ASYNC_SPD_MASK	0x1030
+#define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ASYNC_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
+#define ASYNC_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ASYNC_HARDPPS_CD	0x0800	/* Call hardpps when CD goes high  */
+
+#define ASYNC_SPD_SHI	0x1000	/* Use 230400 instead of 38400 bps */
+#define ASYNC_SPD_WARP	0x1010	/* Use 460800 instead of 38400 bps */
+
+#define ASYNC_FLAGS	0x1FFF	/* Possible legal async flags */
+#define ASYNC_USR_MASK 0x1430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by drivers/char/m68kserial.c */
+#define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ASYNC_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ASYNC_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ASYNC_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ASYNC_CLOSING		0x08000000 /* Serial port is closing */
+#define ASYNC_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ASYNC_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+#define ASYNC_INTERNAL_FLAGS	0xFF000000 /* Internal flags */
+
+/*
+ * Serial input interrupt line counters -- external structure
+ * Four lines can interrupt: CTS, DSR, RI, DCD
+ */
+struct serial_icounter_struct {
+	int cts, dsr, rng, dcd;
+	int rx, tx;
+	int frame, overrun, parity, brk;
+	int buf_overrun;
+	int reserved[9];
+};
+
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+#include <linux/config.h>	/* for Mac SCC extensions */
+
+#ifdef CONFIG_MAC
+#define NUM_ZSREGS    16
+struct mac_zschannel {
+	volatile unsigned char *control;
+	volatile unsigned char *data;
+};
+struct m68k_async_private;
+#endif
+
+struct m68k_async_struct {
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			hub6;		/* HUB6 plus one */
+	int			type;
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	int			IER; 	/* Interrupt Enable Register */
+	int			MCR; 	/* Modem control register */
+	int			MCR_noint; /* MCR with interrupts off */
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct wait_queue	*delta_msr_wait;
+	struct async_icount	icount;	/* kernel counters for the 4 input interrupts */
+	struct m68k_async_struct	*next_port; /* For the linked list */
+	struct m68k_async_struct	*prev_port;
+	void			*board_base; /* board-base address for use with
+						boards carrying several UART's,
+						like some Amiga boards. */
+	unsigned short		nr_uarts;    /* UART-counter, that indicates
+						how many UART's there are on
+						the board.  If the board has a
+						IRQ-register, this can be used
+						to check if any of the uarts,
+						on the board has requested an
+						interrupt, instead of checking
+						IRQ-registers on all UART's */
+	SERIALSWITCH		*sw;		/* functions to manage this port */
+#ifdef CONFIG_MAC
+	struct m68k_async_private	*private;
+#endif
+};
+
+#ifdef CONFIG_MAC
+struct m68k_async_private {
+	struct m68k_async_info	*zs_next;	/* For IRQ servicing chain */
+	struct mac_zschannel	*zs_channel;	/* Channel registers */
+	struct mac_zschannel	*zs_chan_a;	/* A side registers */
+	unsigned char		read_reg_zero;
+
+	char			soft_carrier;	/* Use soft carrier on this */
+	char			break_abort;	/* console, process brk/abrt */
+	char			kgdb_channel;	/* Kgdb running on this channel */
+	char			is_cons;	/* Is this our console. */
+	unsigned char		tx_active;	/* character being xmitted */
+	unsigned char		tx_stopped;	/* output is suspended */
+
+	/* We need to know the current clock divisor
+	 * to read the bps rate the chip has currently
+	 * loaded.
+	 */
+	unsigned char		clk_divisor;	/* May be 1, 16, 32, or 64 */
+	int			zs_baud;
+
+	/* Current write register values */
+	unsigned char		curregs[NUM_ZSREGS];
+
+	/* Values we need to set next opportunity */
+	unsigned char		pendregs[NUM_ZSREGS];
+
+	char			change_needed;
+};
+#endif
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Export to allow PCMCIA to use this - Dave Hinds */
+extern int m68k_register_serial(struct serial_struct *req);
+extern void m68k_unregister_serial(int line);
+extern int register_serial(struct serial_struct *req);
+extern void unregister_serial(int line);
+extern struct m68k_async_struct rs_table[];
+extern task_queue tq_serial;
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static __inline__ void rs_sched_event(struct m68k_async_struct *info, int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static __inline__ void rs_receive_char( struct m68k_async_struct *info,
+					    int ch, int err )
+{
+	struct tty_struct *tty = info->tty;
+	
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+		return;
+	tty->flip.count++;
+	switch(err) {
+	case TTY_BREAK:
+		info->icount.brk++;
+		if (info->flags & ASYNC_SAK)
+			do_SAK(tty);
+		break;
+	case TTY_PARITY:
+		info->icount.parity++;
+		break;
+	case TTY_OVERRUN:
+		info->icount.overrun++;
+		break;
+	case TTY_FRAME:
+		info->icount.frame++;
+		break;
+	}
+	*tty->flip.flag_buf_ptr++ = err;
+	*tty->flip.char_buf_ptr++ = ch;
+	info->icount.rx++;
+	tty_flip_buffer_push(tty);
+}
+
+static __inline__ int rs_get_tx_char( struct m68k_async_struct *info )
+{
+	unsigned char ch;
+	
+	if (info->x_char) {
+		ch = info->x_char;
+		info->icount.tx++;
+		info->x_char = 0;
+		return( ch );
+	}
+
+	if (info->xmit_cnt <= 0 || info->tty->stopped || info->tty->hw_stopped)
+		return( -1 );
+
+	ch = info->xmit_buf[info->xmit_tail++];
+	info->xmit_tail &= SERIAL_XMIT_SIZE - 1;
+	info->icount.tx++;
+	if (--info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+	return( ch );
+}
+
+static __inline__ int rs_no_more_tx( struct m68k_async_struct *info )
+{
+	return( info->xmit_cnt <= 0 ||
+			info->tty->stopped ||
+			info->tty->hw_stopped );
+}
+
+static __inline__ void rs_dcd_changed( struct m68k_async_struct *info, int dcd )
+
+{
+	/* update input line counter */
+	info->icount.dcd++;
+	wake_up_interruptible(&info->delta_msr_wait);
+
+	if (info->flags & ASYNC_CHECK_CD) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttyS%d CD now %s...", info->line,
+		       dcd ? "on" : "off");
+#endif		
+		if (dcd) {
+			wake_up_interruptible(&info->open_wait);
+		} else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			     (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+			if (info->tty)
+				tty_hangup(info->tty);
+		}
+	}
+}
+
+
+void rs_stop( struct tty_struct *tty );
+void rs_start( struct tty_struct *tty );
+
+static __inline__ void rs_check_cts( struct m68k_async_struct *info, int cts )
+{
+	/* update input line counter */
+	info->icount.cts++;
+	wake_up_interruptible(&info->delta_msr_wait);
+	
+	if ((info->flags & ASYNC_CTS_FLOW) && info->tty) {
+		if (info->tty->hw_stopped) {
+			if (cts) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				rs_start( info->tty );
+				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!cts) {
+				info->tty->hw_stopped = 1;
+				rs_stop( info->tty );
+			}
+		}
+	}
+}
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _M68K_SERIAL_H */
+
diff -urN linux-2.2.18-pre15/include/asm-i386/mac_iop.h linux/include/asm-i386/mac_iop.h
--- linux-2.2.18-pre15/include/asm-i386/mac_iop.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/mac_iop.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,162 @@
+/*
+ * I/O Processor (IOP) defines and structures, mostly snagged from A/UX
+ * header files.
+ *
+ * The original header from which this was taken is copyrighted. I've done some
+ * rewriting (in fact my changes make this a bit more readable, IMHO) but some
+ * more should be done.
+ */
+
+/*
+ * This is the base address of the IOPs. Use this as the address of
+ * a "struct iop" (see below) to see where the actual registers fall.
+ */
+
+#define SCC_IOP_BASE_IIFX	(0x50F04000)
+#define ISM_IOP_BASE_IIFX	(0x50F12000)
+
+#define SCC_IOP_BASE_QUADRA	(0x50F0C000)
+#define ISM_IOP_BASE_QUADRA	(0x50F1E000)
+
+/* IOP status/control register bits: */
+
+#define	IOP_BYPASS	0x01	/* bypass-mode hardware access */
+#define	IOP_AUTOINC	0x02	/* allow autoincrement of ramhi/lo */
+#define	IOP_RUN		0x04	/* set to 0 to reset IOP chip */
+#define	IOP_IRQ		0x08	/* generate IRQ to IOP if 1 */
+#define	IOP_INT0	0x10	/* intr priority from IOP to host */
+#define	IOP_INT1	0x20	/* intr priority from IOP to host */
+#define	IOP_HWINT	0x40	/* IRQ from hardware; bypass mode only */
+#define	IOP_DMAINACTIVE	0x80	/* no DMA request active; bypass mode only */
+
+#define NUM_IOPS	2
+#define NUM_IOP_CHAN	7
+#define NUM_IOP_MSGS	NUM_IOP_CHAN*8
+#define IOP_MSG_LEN	32
+
+/* IOP reference numbers, used by the globally-visible iop_xxx functions */
+
+#define IOP_NUM_SCC	0
+#define IOP_NUM_ISM	1
+
+/* IOP channel states */
+
+#define IOP_MSG_IDLE		0       /* idle                         */
+#define IOP_MSG_NEW		1       /* new message sent             */
+#define IOP_MSG_RCVD		2       /* message received; processing */
+#define IOP_MSG_COMPLETE	3       /* message processing complete  */
+
+/* IOP message status codes */
+
+#define IOP_MSGSTATUS_UNUSED	0	/* Unusued message structure       */
+#define IOP_MSGSTATUS_WAITING	1	/* waiting for channel             */
+#define IOP_MSGSTATUS_SENT	2	/* message sent, awaiting reply    */
+#define IOP_MSGSTATUS_COMPLETE	3	/* message complete and reply rcvd */
+#define IOP_MSGSTATUS_UNSOL	6	/* message is unsolicited          */
+
+/* IOP memory addresses of the members of the the mac_iop_kernel structure. */
+
+#define IOP_ADDR_MAX_SEND_CHAN	0x0200
+#define IOP_ADDR_SEND_STATE	0x0201
+#define IOP_ADDR_PATCH_CTRL	0x021F
+#define IOP_ADDR_SEND_MSG	0x0220
+#define IOP_ADDR_MAX_RECV_CHAN	0x0300
+#define IOP_ADDR_RECV_STATE	0x0301
+#define IOP_ADDR_ALIVE		0x031F
+#define IOP_ADDR_RECV_MSG	0x0320
+
+#ifndef __ASSEMBLY__
+
+/*
+ * IOP Control registers, staggered because in usual Apple style they were
+ * too lazy to decode the A0 bit. This structure is assumed to begin at
+ * one of the xxx_IOP_BASE addresses given above.
+ */
+
+struct mac_iop {
+    __u8	ram_addr_hi;	/* shared RAM address hi byte */
+    __u8	pad0;
+    __u8	ram_addr_lo;	/* shared RAM address lo byte */
+    __u8	pad1;
+    __u8	status_ctrl;	/* status/control register */
+    __u8	pad2[3];
+    __u8	ram_data;	/* RAM data byte at ramhi/lo */
+
+    __u8	pad3[23];
+
+    /* Bypass-mode hardware access registers */
+
+    union {
+	struct {		/* SCC registers */
+	    __u8 sccb_cmd;	/* SCC B command reg */
+	    __u8 pad4;
+	    __u8 scca_cmd;	/* SCC A command reg */
+	    __u8 pad5;
+	    __u8 sccb_data;	/* SCC B data */
+	    __u8 pad6;
+	    __u8 scca_data;	/* SCC A data */
+	} scc_regs;
+
+	struct {		/* ISM registers */
+	    __u8 wdata;		/* write a data byte */
+	    __u8 pad7;
+	    __u8 wmark;		/* write a mark byte */
+	    __u8 pad8;
+	    __u8 wcrc;		/* write 2-byte crc to disk */
+	    __u8 pad9;
+	    __u8 wparams;	/* write the param regs */
+	    __u8 pad10;
+	    __u8 wphase;	/* write the phase states & dirs */
+	    __u8 pad11;
+	    __u8 wsetup;	/* write the setup register */
+	    __u8 pad12;
+	    __u8 wzeroes;	/* mode reg: 1's clr bits, 0's are x */
+	    __u8 pad13;
+	    __u8 wones;		/* mode reg: 1's set bits, 0's are x */
+	    __u8 pad14;
+	    __u8 rdata;		/* read a data byte */
+	    __u8 pad15;
+	    __u8 rmark;		/* read a mark byte */
+	    __u8 pad16;
+	    __u8 rerror;	/* read the error register */
+	    __u8 pad17;
+	    __u8 rparams;	/* read the param regs */
+	    __u8 pad18;
+	    __u8 rphase;	/* read the phase states & dirs */
+	    __u8 pad19;
+	    __u8 rsetup;	/* read the setup register */
+	    __u8 pad20;
+	    __u8 rmode;		/* read the mode register */
+	    __u8 pad21;
+	    __u8 rhandshake;	/* read the handshake register */
+	} ism_regs;
+    } b;
+};
+
+/* This structure is used to track IOP messages in the Linux kernel */
+
+struct iop_msg {
+	struct iop_msg	*next;		/* next message in queue or NULL     */
+	uint	iop_num;		/* IOP number                        */
+	uint	channel;		/* channel number                    */
+	void	*caller_priv;		/* caller private data               */
+	int	status;			/* status of this message            */
+	__u8	message[IOP_MSG_LEN];	/* the message being sent/received   */
+	__u8	reply[IOP_MSG_LEN];	/* the reply to the message          */
+	void	(*handler)(struct iop_msg *, struct pt_regs *);
+					/* function to call when reply recvd */
+};
+
+extern int iop_scc_present,iop_ism_present;
+
+extern int iop_listen(uint, uint,
+			void (*handler)(struct iop_msg *, struct pt_regs *),
+			const char *);
+extern int iop_send_message(uint, uint, void *, uint, __u8 *,
+			    void (*)(struct iop_msg *, struct pt_regs *));
+extern void iop_complete_message(struct iop_msg *);
+extern void iop_upload_code(uint, __u8 *, uint, __u16);
+extern void iop_download_code(uint, __u8 *, uint, __u16);
+extern __u8 *iop_compare_code(uint, __u8 *, uint, __u16);
+
+#endif /* __ASSEMBLY__ */
diff -urN linux-2.2.18-pre15/include/asm-i386/mac_oss.h linux/include/asm-i386/mac_oss.h
--- linux-2.2.18-pre15/include/asm-i386/mac_oss.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/mac_oss.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,94 @@
+/*
+ *	OSS
+ *
+ *	This is used in place of VIA2 on the IIfx.
+ */
+ 
+#define OSS_BASE	(0x50f1a000)
+
+/*
+ * Interrupt level offsets for mac_oss->irq_level
+ */
+
+#define OSS_NUBUS0	0
+#define OSS_NUBUS1	1
+#define OSS_NUBUS2	2
+#define OSS_NUBUS3	3
+#define OSS_NUBUS4	4
+#define OSS_NUBUS5	5
+#define OSS_IOPISM	6
+#define OSS_IOPSCC	7
+#define OSS_SOUND	8
+#define OSS_SCSI	9
+#define OSS_60HZ	10
+#define OSS_VIA1	11
+#define OSS_UNUSED1	12
+#define OSS_UNUSED2	13
+#define OSS_PARITY	14
+#define OSS_UNUSED3	15
+
+#define OSS_NUM_SOURCES	16
+
+/*
+ * Pending interrupt bits in mac_oss->irq_pending
+ */
+
+#define OSS_IP_NUBUS0	0x0001
+#define OSS_IP_NUBUS1	0x0002
+#define OSS_IP_NUBUS2	0x0004
+#define OSS_IP_NUBUS3	0x0008
+#define OSS_IP_NUBUS4	0x0010
+#define OSS_IP_NUBUS5	0x0020
+#define OSS_IP_IOPISM	0x0040
+#define OSS_IP_IOPSCC	0x0080
+#define OSS_IP_SOUND	0x0100
+#define OSS_IP_SCSI	0x0200
+#define OSS_IP_60HZ	0x0400
+#define OSS_IP_VIA1	0x0800
+#define OSS_IP_UNUSED1	0x1000
+#define OSS_IP_UNUSED2	0x2000
+#define OSS_IP_PARITY	0x4000
+#define OSS_IP_UNUSED3	0x8000
+
+#define OSS_IP_NUBUS (OSS_IP_NUBUS0|OSS_IP_NUBUS1|OSS_IP_NUBUS2|OSS_IP_NUBUS3|OSS_IP_NUBUS4|OSS_IP_NUBUS5)
+
+/*
+ * Rom Control Register
+ */
+
+#define OSS_POWEROFF	0x80
+
+/*
+ * OSS Interrupt levels for various sub-systems
+ *
+ * This mapping is layed out with two things in mind: first, we try to keep
+ * things on their own levels to avoid having to do double-dispatches. Second,
+ * the levels match as closely as possible the alternate IRQ mapping mode (aka
+ * "A/UX mode") available on some VIA machines.
+ */
+
+#define OSS_IRQLEV_DISABLED	0
+#define OSS_IRQLEV_IOPISM	1	/* ADB? */
+#define OSS_IRQLEV_SCSI		2
+#define OSS_IRQLEV_NUBUS	3	/* keep this on its own level */
+#define OSS_IRQLEV_IOPSCC	4	/* matches VIA alternate mapping */
+#define OSS_IRQLEV_SOUND	5	/* matches VIA alternate mapping */
+#define OSS_IRQLEV_60HZ		6	/* matches VIA alternate mapping */
+#define OSS_IRQLEV_VIA1		6	/* matches VIA alternate mapping */
+#define OSS_IRQLEV_PARITY	7	/* matches VIA alternate mapping */
+
+#ifndef __ASSEMBLY__
+
+struct mac_oss {
+    __u8  irq_level[0x10];	/* [0x000-0x00f] Interrupt levels */
+    __u8  padding0[0x1F2];	/* [0x010-0x201] IO space filler */
+    __u16 irq_pending;		/* [0x202-0x203] pending interrupts bits */
+    __u8  rom_ctrl;		/* [0x204-0x204] ROM cntl reg (for poweroff) */
+    __u8  padding1[0x2];	/* [0x205-0x206] currently unused by A/UX */
+    __u8  ack_60hz;		/* [0x207-0x207] 60 Hz ack. */
+};
+
+extern volatile struct mac_oss *oss;
+extern int oss_present;
+
+#endif /* __ASSEMBLY__ */
diff -urN linux-2.2.18-pre15/include/asm-i386/mac_via.h linux/include/asm-i386/mac_via.h
--- linux-2.2.18-pre15/include/asm-i386/mac_via.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/mac_via.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,300 @@
+/*
+ *	6522 Versatile Interface Adapter (VIA)
+ *
+ *	There are two of these on the Mac II. Some IRQ's are vectored
+ *	via them as are assorted bits and bobs - eg rtc, adb. The picture
+ *	is a bit incomplete as the Mac documentation doesnt cover this well
+ */
+ 
+#ifndef _ASM_VIA6522_H_
+#define _ASM_VIA6522_H_
+
+/*
+ * Base addresses for the VIAs. There are two in every machine,
+ * although on some machines the second is an RBV or an OSS.
+ * The OSS is different enough that it's handled separately.
+ *
+ * Do not use these values directly; use the via1 and via2 variables
+ * instead (and don't forget to check rbv_present when using via2!)
+ */
+
+#define VIA1_BASE	(0x50F00000)
+#define VIA2_BASE	(0x50F02000)
+#define  RBV_BASE	(0x50F26000)
+ 
+/*
+ *	Not all of these are true post MacII I think.
+ *      CSA: probably the ones CHRP marks as 'unused' change purposes
+ *      when the IWM becomes the SWIM.
+ *      http://www.rs6000.ibm.com/resource/technology/chrpio/via5.mak.html
+ *      ftp://ftp.austin.ibm.com/pub/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf
+ *
+ * also, http://developer.apple.com/technotes/hw/hw_09.html claims the
+ * following changes for IIfx:
+ * VIA1A_vSccWrReq not available and that VIA1A_vSync has moved to an IOP.
+ * Also, "All of the functionality of VIA2 has been moved to other chips".
+ */
+ 
+#define VIA1A_vSccWrReq	0x80	/* SCC write. (input)
+				 * [CHRP] SCC WREQ: Reflects the state of the 
+				 * Wait/Request pins from the SCC.
+				 * [Macintosh Family Hardware]
+				 * as CHRP on SE/30,II,IIx,IIcx,IIci.
+				 * on IIfx, "0 means an active request"
+				 */
+#define VIA1A_vRev8	0x40	/* Revision 8 board ???
+                                 * [CHRP] En WaitReqB: Lets the WaitReq_L
+				 * signal from port B of the SCC appear on
+				 * the PA7 input pin. Output.
+				 * [Macintosh Family] On the SE/30, this
+				 * is the bit to flip screen buffers.
+				 * 0=alternate, 1=main.
+				 * on II,IIx,IIcx,IIci,IIfx this is a bit
+				 * for Rev ID. 0=II,IIx, 1=IIcx,IIci,IIfx
+				 */
+#define VIA1A_vHeadSel	0x20	/* Head select for IWM.
+				 * [CHRP] unused.
+				 * [Macintosh Family] "Floppy disk
+				 * state-control line SEL" on all but IIfx
+				 */
+#define VIA1A_vOverlay	0x10    /* [Macintosh Family] On SE/30,II,IIx,IIcx
+				 * this bit enables the "Overlay" address
+				 * map in the address decoders as it is on
+				 * reset for mapping the ROM over the reset
+				 * vector. 1=use overlay map.
+				 * On the IIci,IIfx it is another bit of the
+				 * CPU ID: 0=normal IIci, 1=IIci with parity
+				 * feature or IIfx.
+				 * [CHRP] En WaitReqA: Lets the WaitReq_L
+				 * signal from port A of the SCC appear
+				 * on the PA7 input pin (CHRP). Output.
+				 * [MkLinux] "Drive Select"
+				 *  (with 0x20 being 'disk head select')
+				 */
+#define VIA1A_vSync	0x08    /* [CHRP] Sync Modem: modem clock select:
+                                 * 1: select the external serial clock to
+				 *    drive the SCC's /RTxCA pin.
+				 * 0: Select the 3.6864MHz clock to drive
+				 *    the SCC cell.
+				 * [Macintosh Family] Correct on all but IIfx
+				 */
+
+/* Macintosh Family Hardware sez: bits 0-2 of VIA1A are volume control
+ * on Macs which had the PWM sound hardware.  Reserved on newer models.
+ * On IIci,IIfx, bits 1-2 are the rest of the CPU ID:
+ * bit 2: 1=IIci, 0=IIfx
+ * bit 1: 1 on both IIci and IIfx.
+ * MkLinux sez bit 0 is 'burnin flag' in this case.
+ * CHRP sez: VIA1A bits 0-2 and 5 are 'unused': if programmed as
+ * inputs, these bits will read 0.
+ */
+#define VIA1A_vVolume	0x07	/* Audio volume mask for PWM */
+#define VIA1A_CPUID0	0x02	/* CPU id bit 0 on RBV, others */
+#define VIA1A_CPUID1	0x04	/* CPU id bit 0 on RBV, others */
+#define VIA1A_CPUID2	0x10	/* CPU id bit 0 on RBV, others */
+#define VIA1A_CPUID3	0x40	/* CPU id bit 0 on RBV, others */
+ 	
+/* Info on VIA1B is from Macintosh Family Hardware & MkLinux.
+ * CHRP offers no info. */
+#define VIA1B_vSound	0x80	/* Sound enable (for compatibility with
+				 * PWM hardware) 0=enabled.
+				 * Also, on IIci w/parity, shows parity error
+				 * 0=error, 1=OK. */
+#define VIA1B_vMystery	0x40    /* On IIci, parity enable. 0=enabled,1=disabled
+				 * On SE/30, vertical sync interrupt enable.
+				 * 0=enabled. This vSync interrupt shows up
+				 * as a slot $E interrupt. */
+#define VIA1B_vADBS2	0x20	/* ADB state input bit 1 (unused on IIfx) */
+#define VIA1B_vADBS1	0x10	/* ADB state input bit 0 (unused on IIfx) */
+#define VIA1B_vADBInt	0x08	/* ADB interrupt 0=interrupt (unused on IIfx)*/
+#define VIA1B_vRTCEnb	0x04	/* Enable Real time clock. 0=enabled. */
+#define VIA1B_vRTCClk	0x02    /* Real time clock serial-clock line. */
+#define VIA1B_vRTCData	0x01    /* Real time clock serial-data line. */
+
+/* MkLinux defines the following "VIA1 Register B contents where they
+ * differ from standard VIA1".  From the naming scheme, we assume they
+ * correspond to a VIA work-alike named 'EVR'. */
+#define	EVRB_XCVR	0x08	/* XCVR_SESSION* */
+#define	EVRB_FULL	0x10	/* VIA_FULL */
+#define	EVRB_SYSES	0x20	/* SYS_SESSION */
+#define	EVRB_AUXIE	0x00	/* Enable A/UX Interrupt Scheme */
+#define	EVRB_AUXID	0x40	/* Disable A/UX Interrupt Scheme */
+#define	EVRB_SFTWRIE	0x00	/* Software Interrupt ReQuest */
+#define	EVRB_SFTWRID	0x80	/* Software Interrupt ReQuest */
+
+/*
+ *	VIA2 A register is the interrupt lines raised off the nubus 
+ *	slots.
+ *      The below info is from 'Macintosh Family Hardware.'
+ *      MkLinux calls the 'IIci internal video IRQ' below the 'RBV slot 0 irq.'
+ *      It also notes that the slot $9 IRQ is the 'Ethernet IRQ' and
+ *      defines the 'Video IRQ' as 0x40 for the 'EVR' VIA work-alike.
+ *      Perhaps OSS uses vRAM1 and vRAM2 for ADB.
+ */
+ 
+#define VIA2A_vRAM1	0x80	/* RAM size bit 1 (IIci: reserved) */
+#define VIA2A_vRAM0	0x40	/* RAM size bit 0 (IIci: internal video IRQ) */
+#define VIA2A_vIRQE	0x20	/* IRQ from slot $E */
+#define VIA2A_vIRQD	0x10	/* IRQ from slot $D */
+#define VIA2A_vIRQC	0x08	/* IRQ from slot $C */
+#define VIA2A_vIRQB	0x04	/* IRQ from slot $B */
+#define VIA2A_vIRQA	0x02	/* IRQ from slot $A */
+#define VIA2A_vIRQ9	0x01	/* IRQ from slot $9 */
+
+/* RAM size bits decoded as follows:
+ * bit1 bit0  size of ICs in bank A
+ *  0    0    256 kbit
+ *  0    1    1 Mbit
+ *  1    0    4 Mbit
+ *  1    1   16 Mbit
+ */
+ 
+/*
+ *	Register B has the fun stuff in it
+ */
+ 
+#define VIA2B_vVBL	0x80	/* VBL output to VIA1 (60.15Hz) driven by
+				 * timer T1.
+				 * on IIci, parity test: 0=test mode.
+				 * [MkLinux] RBV_PARODD: 1=odd,0=even. */
+#define VIA2B_vSndJck	0x40	/* External sound jack status.
+				 * 0=plug is inserted.  On SE/30, always 0 */
+#define VIA2B_vTfr0	0x20	/* Transfer mode bit 0 ack from NuBus */
+#define VIA2B_vTfr1	0x10	/* Transfer mode bit 1 ack from NuBus */
+#define VIA2B_vMode32	0x08	/* 24/32bit switch - doubles as cache flush
+				 * on II, AMU/PMMU control.
+				 *   if AMU, 0=24bit to 32bit translation
+				 *   if PMMU, 1=PMMU is accessing page table.
+				 * on SE/30 tied low.
+				 * on IIx,IIcx,IIfx, unused.
+				 * on IIci/RBV, cache control. 0=flush cache.
+				 */
+#define VIA2B_vPower	0x04	/* Power off, 0=shut off power.
+				 * on SE/30 this signal sent to PDS card. */
+#define VIA2B_vBusLk	0x02	/* Lock NuBus transactions, 0=locked.
+				 * on SE/30 sent to PDS card. */
+#define VIA2B_vCDis	0x01	/* Cache control. On IIci, 1=disable cache card
+				 * on others, 0=disable processor's instruction
+				 * and data caches. */
+
+/* Apple sez: http://developer.apple.com/technotes/ov/ov_04.html
+ * Another example of a valid function that has no ROM support is the use
+ * of the alternate video page for page-flipping animation. Since there
+ * is no ROM call to flip pages, it is necessary to go play with the
+ * right bit in the VIA chip (6522 Versatile Interface Adapter).
+ * [CSA: don't know which one this is, but it's one of 'em!]
+ */
+ 
+/*
+ *	6522 registers - see databook.
+ * CSA: Assignments for VIA1 confirmed from CHRP spec.
+ */ 
+
+/* partial address decode.  0xYYXX : XX part for RBV, YY part for VIA */
+/* Note: 15 VIA regs, 8 RBV regs */
+
+#define vBufB	0x0000	/* [VIA/RBV]  Register B */
+#define vBufAH	0x0200  /* [VIA only] Buffer A, with handshake. DON'T USE! */
+#define vDirB	0x0400  /* [VIA only] Data Direction Register B. */
+#define vDirA	0x0600  /* [VIA only] Data Direction Register A. */
+#define vT1CL	0x0800  /* [VIA only] Timer one counter low. */
+#define vT1CH	0x0a00  /* [VIA only] Timer one counter high. */
+#define vT1LL	0x0c00  /* [VIA only] Timer one latches low. */
+#define vT1LH	0x0e00  /* [VIA only] Timer one latches high. */
+#define vT2CL	0x1000  /* [VIA only] Timer two counter low. */
+#define vT2CH	0x1200  /* [VIA only] Timer two counter high. */
+#define vSR	0x1400  /* [VIA only] Shift register. */
+#define vACR	0x1600  /* [VIA only] Auxilary control register. */
+#define vPCR	0x1800  /* [VIA only] Peripheral control register. */
+                        /*            CHRP sez never ever to *write* this.
+			 *            Mac family says never to *change* this.
+			 * In fact we need to initialize it once at start. */
+#define vIFR	0x1a00  /* [VIA/RBV]  Interrupt flag register. */
+#define vIER	0x1c00  /* [VIA/RBV]  Interrupt enable register. */
+#define vBufA	0x1e00  /* [VIA/RBV] register A (no handshake) */
+
+/* The RBV only decodes the bottom eight address lines; the VIA doesn't
+ * decode the bottom eight -- so vBufB | rBufB will always get you BufB */
+/* CSA: in fact, only bits 0,1, and 4 seem to be decoded.
+ * BUT note the values for rIER and rIFR, where the top 8 bits *do* seem
+ * to matter.  In fact *all* of the top 8 bits seem to matter; 
+ * setting rIER=0x1813 and rIFR=0x1803 doesn't work, either.
+ * Perhaps some sort of 'compatibility mode' is built-in? [21-May-1999]
+ */
+
+#define rBufB   0x0000  /* [VIA/RBV]  Register B */
+#define rExp	0x0001	/* [RBV only] RBV future expansion (always 0) */
+#define rSIFR	0x0002  /* [RBV only] RBV slot interrupts register. */
+#define rIFR	0x1a03  /* [VIA/RBV]  RBV interrupt flag register. */
+#define rMonP   0x0010  /* [RBV only] RBV video monitor type. */
+#define rChpT   0x0011  /* [RBV only] RBV test mode register (reads as 0). */
+#define rSIER   0x0012  /* [RBV only] RBV slot interrupt enables. */
+#define rIER    0x1c13  /* [VIA/RBV]  RBV interrupt flag enable register. */
+#define rBufA	rSIFR   /* the 'slot interrupts register' is BufA on a VIA */
+
+/*
+ * Video monitor parameters, for rMonP:
+ */
+#define RBV_DEPTH  0x07	/* bits per pixel: 000=1,001=2,010=4,011=8 */
+#define RBV_MONID  0x38	/* monitor type, as below. */
+#define RBV_VIDOFF 0x40	/* 1 turns off onboard video */
+/* Supported monitor types: */
+#define MON_15BW   (1<<3) /* 15" BW portrait. */
+#define MON_IIGS   (2<<3) /* 12" color (modified IIGS monitor). */
+#define MON_15RGB  (5<<3) /* 15" RGB portrait. */
+#define MON_12OR13 (6<<3) /* 12" BW or 13" RGB. */
+#define MON_NONE   (7<<3) /* No monitor attached. */
+
+/* To clarify IER manipulations */
+#define IER_SET_BIT(b) (0x80 | (1<<(b)) )
+#define IER_CLR_BIT(b) (0x7F & (1<<(b)) )
+
+#ifndef __ASSEMBLY__
+
+extern volatile __u8 *via1,*via2;
+extern int rbv_present,via_alt_mapping;
+extern __u8 rbv_clear;
+
+#if 0
+/*
+ *	The 6522 via is a 2MHz part, and needs a delay. MacOS seems to
+ *	execute MOV (Ax),(Ax) for this... Oh and we can't use udelay
+ *	here... see we need the via to calibrate the udelay loop ...
+ */
+
+/* NetBSD doesn't use this, and it doesn't seem to make a difference. */
+extern volatile long *via_memory_bogon;
+#endif
+
+extern __u8 *via_get_base_address(int);
+
+extern __inline__ void via_write(volatile __u8 *via,int reg, int v)
+{
+#if 0
+	*via_memory_bogon;
+	*via_memory_bogon;
+	*via_memory_bogon;
+#endif
+	via[reg]=v;
+}
+
+extern __inline__ int via_read(volatile __u8 *via,int reg)
+{
+#if 0
+	*via_memory_bogon;
+	*via_memory_bogon;
+	*via_memory_bogon;
+#endif
+	return (int)via[reg];
+}
+
+extern __inline__ int rbv_set_video_bpp(int bpp) {
+	char val = (bpp==1)?0:(bpp==2)?1:(bpp==4)?2:(bpp==8)?3:-1;
+	if (!rbv_present || val<0) return -1;
+	via_write(via2, rMonP, val | (via_read(via2, rMonP)&(~RBV_DEPTH)));
+	return 0;
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_VIA6522_H_ */
diff -urN linux-2.2.18-pre15/include/asm-i386/math-emu.h linux/include/asm-i386/math-emu.h
--- linux-2.2.18-pre15/include/asm-i386/math-emu.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/math-emu.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,300 @@
+#ifndef _ASM_M68K_SETUP_H
+#define _ASM_M68K_SETUP_H
+
+#include <asm/setup.h>
+#include <linux/linkage.h>
+
+/* Status Register bits */
+
+/* accrued exception bits */
+#define FPSR_AEXC_INEX	3
+#define FPSR_AEXC_DZ	4
+#define FPSR_AEXC_UNFL	5
+#define FPSR_AEXC_OVFL	6
+#define FPSR_AEXC_IOP	7
+
+/* exception status bits */
+#define FPSR_EXC_INEX1	8
+#define FPSR_EXC_INEX2	9
+#define FPSR_EXC_DZ	10
+#define FPSR_EXC_UNFL	11
+#define FPSR_EXC_OVFL	12
+#define FPSR_EXC_OPERR	13
+#define FPSR_EXC_SNAN	14
+#define FPSR_EXC_BSUN	15
+
+/* quotient byte, assumes big-endian, of course */
+#define FPSR_QUOTIENT(fpsr) (*((signed char *) &(fpsr) + 1))
+
+/* condition code bits */
+#define FPSR_CC_NAN	24
+#define FPSR_CC_INF	25
+#define FPSR_CC_Z	26
+#define FPSR_CC_NEG	27
+
+
+/* Control register bits */
+
+/* rounding mode */
+#define	FPCR_ROUND_RN	0		/* round to nearest/even */
+#define FPCR_ROUND_RZ	1		/* round to zero */
+#define FPCR_ROUND_RM	2		/* minus infinity */
+#define FPCR_ROUND_RP	3		/* plus infinity */
+
+/* rounding precision */
+#define FPCR_PRECISION_X	0	/* long double */
+#define FPCR_PRECISION_S	1	/* double */
+#define FPCR_PRECISION_D	2	/* float */
+
+
+/* Flags to select the debugging output */
+#define PDECODE		0
+#define PEXECUTE	1
+#define PCONV		2
+#define PNORM		3
+#define PREGISTER	4
+#define PINSTR		5
+#define PUNIMPL		6
+#define PMOVEM		7
+
+#define PMDECODE	(1<<PDECODE)
+#define PMEXECUTE	(1<<PEXECUTE)
+#define PMCONV		(1<<PCONV)
+#define PMNORM		(1<<PNORM)
+#define PMREGISTER	(1<<PREGISTER)
+#define PMINSTR		(1<<PINSTR)
+#define PMUNIMPL	(1<<PUNIMPL)
+#define PMMOVEM		(1<<PMOVEM)
+
+#ifndef __ASSEMBLY__
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+union fp_mant64 {
+	unsigned long long m64;
+	unsigned long m32[2];
+};
+
+union fp_mant128 {
+	unsigned long long m64[2];
+	unsigned long m32[4];
+};
+
+/* internal representation of extended fp numbers */
+struct fp_ext {
+	unsigned char lowmant;
+	unsigned char sign;
+	unsigned short exp;
+	union fp_mant64 mant;
+};
+
+/* C representation of FPU registers */
+/* NOTE: if you change this, you have to change the assembler offsets
+   below and the size in <asm/fpu.h>, too */
+struct fp_data {
+	struct fp_ext fpreg[8];
+	unsigned int fpcr;
+	unsigned int fpsr;
+	unsigned int fpiar;
+	unsigned short prec;
+	unsigned short rnd;
+	struct fp_ext temp[2];
+};
+
+#if FPU_EMU_DEBUG
+extern unsigned int fp_debugprint;
+
+#define dprint(bit, fmt, args...) ({			\
+	if (fp_debugprint & (1 << (bit)))		\
+		printk(fmt, ## args);			\
+})
+#else
+#define dprint(bit, fmt, args...)
+#endif
+
+#define uprint(str) ({					\
+	static int __count = 3;				\
+							\
+	if (__count > 0) {				\
+		printk("You just hit an unimplemented "	\
+		       "fpu instruction (%s)\n", str);	\
+		printk("Please report this to ....\n");	\
+		__count--;				\
+	}						\
+})
+
+#define FPDATA		((struct fp_data *)current->tss.fp)
+
+#else	/* __ASSEMBLY__ */
+
+#define FPDATA		%a2
+
+/* offsets from the base register to the floating point data in the task struct */
+#define FPD_FPREG	(TASK_TSS+TSS_FPREG+0)
+#define FPD_FPCR	(TASK_TSS+TSS_FPREG+96)
+#define FPD_FPSR	(TASK_TSS+TSS_FPREG+100)
+#define FPD_FPIAR	(TASK_TSS+TSS_FPREG+104)
+#define FPD_PREC	(TASK_TSS+TSS_FPREG+108)
+#define FPD_RND		(TASK_TSS+TSS_FPREG+110)
+#define FPD_TEMPFP1	(TASK_TSS+TSS_FPREG+112)
+#define FPD_TEMPFP2	(TASK_TSS+TSS_FPREG+124)
+#define FPD_SIZEOF	(TASK_TSS+TSS_FPREG+136)
+
+/* offsets on the stack to access saved registers,
+ * these are only used during instruction decoding
+ * where we always know how deep we're on the stack.
+ */
+#define FPS_DO		(PT_D0)
+#define FPS_D1		(PT_D1)
+#define FPS_D2		(PT_D2)
+#define FPS_A0		(PT_A0)
+#define FPS_A1		(PT_A1)
+#define FPS_A2		(PT_A2)
+#define FPS_SR		(PT_SR)
+#define FPS_PC		(PT_PC)
+#define FPS_EA		(PT_PC+6)
+#define FPS_PC2		(PT_PC+10)
+
+.macro	fp_get_fp_reg
+	lea	(FPD_FPREG,FPDATA,%d0.w*4),%a0
+	lea	(%a0,%d0.w*8),%a0
+.endm
+
+/* Macros used to get/put the current program counter.
+ * 020/030 use a different stack frame then 040/060, for the
+ * 040/060 the return pc points already to the next location,
+ * so this only needs to be modified for jump instructions.
+ */
+.macro	fp_get_pc dest
+	move.l	(FPS_PC+4,%sp),\dest
+.endm
+
+.macro	fp_put_pc src,jump=0
+	move.l	\src,(FPS_PC+4,%sp)
+.endm
+
+.macro	fp_get_instr_data	f,s,dest,label
+	getuser	\f,%sp@(FPS_PC+4)@(0),\dest,\label,%sp@(FPS_PC+4)
+	addq.l	#\s,%sp@(FPS_PC+4)
+.endm
+
+.macro	fp_get_instr_word	dest,label,addr
+	fp_get_instr_data	w,2,\dest,\label,\addr
+.endm
+
+.macro	fp_get_instr_long	dest,label,addr
+	fp_get_instr_data	l,4,\dest,\label,\addr
+.endm
+
+/* These macros are used to read from/write to user space
+ * on error we jump to the fixup section, load the fault
+ * address into %a0 and jump to the exit.
+ * (derived from <asm/uaccess.h>)
+ */
+.macro	getuser	size,src,dest,label,addr
+|	printf	,"[\size<%08x]",1,\addr
+.Lu1\@:	moves\size	\src,\dest
+
+	.section .fixup,"ax"
+	.even
+.Lu2\@:	move.l	\addr,%a0
+	jra	\label
+	.previous
+
+	.section __ex_table,"a"
+	.align	4
+	.long	.Lu1\@,.Lu2\@
+	.previous
+.endm
+
+.macro	putuser	size,src,dest,label,addr
+|	printf	,"[\size>%08x]",1,\addr
+.Lu1\@:	moves\size	\src,\dest
+.Lu2\@:
+
+	.section .fixup,"ax"
+	.even
+.Lu3\@:	move.l	\addr,%a0
+	jra	\label
+	.previous
+
+	.section __ex_table,"a"
+	.align	4
+	.long	.Lu1\@,.Lu3\@
+	.long	.Lu2\@,.Lu3\@
+	.previous
+.endm
+
+
+.macro	movestack	nr,arg1,arg2,arg3,arg4,arg5
+	.if	\nr
+	movestack	(\nr-1),\arg2,\arg3,\arg4,\arg5
+	move.l	\arg1,-(%sp)
+	.endif
+.endm
+
+.macro	printf	bit=-1,string,nr=0,arg1,arg2,arg3,arg4,arg5
+#ifdef FPU_EMU_DEBUG
+	.data
+.Lpdata\@:
+	.string	"\string"
+	.previous
+
+	movem.l	%d0/%d1/%a0/%a1,-(%sp)
+	.if	\bit+1
+#if 0
+	moveq	#\bit,%d0
+	andw	#7,%d0
+	btst	%d0,fp_debugprint+((31-\bit)/8)
+#else
+	btst	#\bit,fp_debugprint+((31-\bit)/8)
+#endif
+	jeq	.Lpskip\@
+	.endif
+	movestack	\nr,\arg1,\arg2,\arg3,\arg4,\arg5
+	pea	.Lpdata\@
+	jsr	printk
+	lea	((\nr+1)*4,%sp),%sp
+.Lpskip\@:
+	movem.l	(%sp)+,%d0/%d1/%a0/%a1
+#endif
+.endm
+
+.macro	printx	bit,fp
+#ifdef FPU_EMU_DEBUG
+	movem.l	%d0/%a0,-(%sp)
+	lea	\fp,%a0
+#if 0
+	moveq	#'+',%d0
+	tst.w	(%a0)
+	jeq	.Lx1\@
+	moveq	#'-',%d0
+.Lx1\@:	printf	\bit," %c",1,%d0
+	move.l	(4,%a0),%d0
+	bclr	#31,%d0
+	jne	.Lx2\@
+	printf	\bit,"0."
+	jra	.Lx3\@
+.Lx2\@:	printf	\bit,"1."
+.Lx3\@:	printf	\bit,"%08x%08x",2,%d0,%a0@(8)
+	move.w	(2,%a0),%d0
+	ext.l	%d0
+	printf	\bit,"E%04x",1,%d0
+#else
+	printf	\bit," %08x%08x%08x",3,%a0@,%a0@(4),%a0@(8)
+#endif
+	movem.l	(%sp)+,%d0/%a0
+#endif
+.endm
+
+.macro	debug	instr,args
+#ifdef FPU_EMU_DEBUG
+	\instr	\args
+#endif
+.endm
+
+
+#endif	/* __ASSEMBLY__ */
+
+#endif	/* _ASM_M68K_SETUP_H */
diff -urN linux-2.2.18-pre15/include/asm-i386/swim_iop.h linux/include/asm-i386/swim_iop.h
--- linux-2.2.18-pre15/include/asm-i386/swim_iop.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/swim_iop.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,221 @@
+/*
+ * SWIM access through the IOP
+ * Written by Joshua M. Thompson
+ */
+
+/* IOP number and channel number for the SWIM */
+
+#define SWIM_IOP	IOP_NUM_ISM
+#define SWIM_CHAN	1
+
+/* Command code: */
+
+#define CMD_INIT		0x01	/* Initialize                  */
+#define CMD_SHUTDOWN		0x02	/* Shutdown                    */
+#define CMD_START_POLL		0x03	/* Start insert/eject polling  */
+#define CMD_STOP_POLL		0x04	/* Stop insert/eject polling   */
+#define CMD_SETHFSTAG		0x05	/* Set HFS tag buffer address  */
+#define CMD_STATUS		0x06	/* Status                      */
+#define CMD_EJECT		0x07	/* Eject                       */
+#define CMD_FORMAT		0x08	/* Format                      */
+#define CMD_FORMAT_VERIFY	0x09	/* Format and Verify           */
+#define CMD_WRITE		0x0A	/* Write                       */
+#define CMD_READ		0x0B	/* Read                        */
+#define CMD_READ_VERIFY		0x0C	/* Read and Verify             */
+#define CMD_CACHE_CTRL		0x0D	/* Cache control               */
+#define CMD_TAGBUFF_CTRL	0x0E	/* Tag buffer control          */
+#define CMD_GET_ICON		0x0F	/* Get Icon                    */
+
+/* Drive types: */
+
+/* note: apple sez DRV_FDHD is 4, but I get back a type */
+/*       of 5 when I do a drive status check on my FDHD */
+
+#define	DRV_NONE	0	/* No drive             */
+#define	DRV_UNKNOWN	1	/* Unspecified drive    */
+#define	DRV_400K	2	/* 400K                 */
+#define	DRV_800K	3	/* 400K/800K            */
+#define	DRV_FDHD	5	/* 400K/800K/720K/1440K */
+#define	DRV_HD20	7	/* Apple HD20           */
+
+/* Format types: */
+
+#define	FMT_HD20	0x0001	/*  Apple HD20 */
+#define	FMT_400K	0x0002	/*  400K (GCR) */
+#define	FMT_800K	0x0004	/*  800K (GCR) */
+#define	FMT_720K	0x0008	/*  720K (MFM) */
+#define	FMT_1440K	0x0010	/* 1.44M (MFM) */
+
+#define	FMD_KIND_400K	1
+#define	FMD_KIND_800K	2
+#define	FMD_KIND_720K	3
+#define	FMD_KIND_1440K	1
+
+/* Icon Flags: */
+
+#define	ICON_MEDIA	0x01	/* Have IOP supply media icon */
+#define	ICON_DRIVE	0x01	/* Have IOP supply drive icon */
+
+/* Error codes: */
+
+#define	gcrOnMFMErr	-400	/* GCR (400/800K) on HD media */
+#define	verErr		-84	/* verify failed */
+#define	fmt2Err		-83	/* cant get enough sync during format */
+#define	fmt1Err		-82	/* can't find sector 0 after track format */
+#define	sectNFErr	-81	/* can't find sector */
+#define	seekErr		-80	/* drive error during seek */
+#define	spdAdjErr	-79	/* can't set drive speed */
+#define	twoSideErr	-78	/* drive is single-sided */
+#define	initIWMErr	-77	/* error during initialization */
+#define	tk0badErr	-76	/* track zero is bad */
+#define	cantStepErr	-75	/* drive error during step */
+#define	wrUnderrun	-74	/* write underrun occurred */
+#define	badDBtSlp	-73	/* bad data bitslip marks */
+#define	badDCksum	-72	/* bad data checksum */
+#define	noDtaMkErr	-71	/* can't find data mark */
+#define	badBtSlpErr	-70	/* bad address bitslip marks */
+#define	badCksmErr	-69	/* bad address-mark checksum */
+#define	dataVerErr	-68	/* read-verify failed */
+#define	noAdrMkErr	-67	/* can't find an address mark */
+#define	noNybErr	-66	/* no nybbles? disk is probably degaussed */
+#define	offLinErr	-65	/* no disk in drive */
+#define	noDriveErr	-64	/* drive isn't connected */
+#define	nsDrvErr	-56	/* no such drive */
+#define	paramErr	-50	/* bad positioning information */
+#define	wPrErr		-44	/* write protected */
+#define	openErr		-23	/* already initialized */
+
+#ifndef __ASSEMBLY__
+
+struct swim_drvstatus {
+	__u16	curr_track;	/* Current track number                   */
+	__u8	write_prot;	/* 0x80 if disk is write protected        */
+	__u8	disk_in_drive;	/* 0x01 or 0x02 if a disk is in the drive */
+	__u8	installed;	/* 0x01 if drive installed, 0xFF if not   */
+	__u8	num_sides;	/* 0x80 if two-sided format supported     */
+	__u8	two_sided;	/* 0xff if two-sided format diskette      */
+	__u8	new_interface;	/* 0x00 if old 400K drive, 0xFF if newer  */
+	__u16	errors;		/* Disk error count                       */
+	struct {		/* 32 bits */
+		__u16	reserved;
+		__u16	:4;
+		__u16	external:1;	/* Drive is external        */
+		__u16	scsi:1;		/* Drive is a SCSI drive    */
+		__u16	fixed:1;	/* Drive has fixed media    */
+		__u16	secondary:1;	/* Drive is secondary drive */
+		__u8	type;		/* Drive type               */
+	} info;
+	__u8	mfm_drive;	/* 0xFF if this is an FDHD drive    */
+	__u8	mfm_disk;	/* 0xFF if 720K/1440K (MFM) disk    */
+	__u8	mfm_format;	/* 0x00 if 720K, 0xFF if 1440K      */
+	__u8	ctlr_type;	/* 0x00 if IWM, 0xFF if SWIM        */
+	__u16	curr_format;	/* Current format type              */
+	__u16	allowed_fmt;	/* Allowed format types             */
+	__u32	num_blocks;	/* Number of blocks on disk         */
+	__u8	icon_flags;	/* Icon flags                       */
+	__u8	unusued;
+};
+
+/* Commands issued from the host to the IOP: */
+
+struct swimcmd_init {
+	__u8	code;		/* CMD_INIT */
+	__u8	unusued;
+	__u16	error;
+	__u8	drives[28];	/* drive type list */
+};
+
+struct swimcmd_startpoll {
+	__u8	code;		/* CMD_START_POLL */
+	__u8	unusued;
+	__u16	error;
+};
+
+struct swimcmd_sethfstag {
+	__u8	code;		/* CMD_SETHFSTAG */
+	__u8	unusued;
+	__u16	error;
+	caddr_t	tagbuf;		/* HFS tag buffer address */
+};
+
+struct swimcmd_status {
+	__u8	code;		/* CMD_STATUS */
+	__u8	drive_num;
+	__u16	error;
+	struct swim_drvstatus status;
+};
+
+struct swimcmd_eject {
+	__u8	code;		/* CMD_EJECT */
+	__u8	drive_num;
+	__u16	error;
+	struct swim_drvstatus status;
+};
+
+struct swimcmd_format {
+	__u8	code;		/* CMD_FORMAT */
+	__u8	drive_num;
+	__u16	error;
+	union {
+		struct {
+			__u16 fmt;	   /* format kind                  */
+			__u8  hdrbyte;	   /* fmt byte for hdr (0=default) */
+			__u8  interleave;  /* interleave (0 = default)     */
+			caddr_t	databuf;   /* sector data buff (0=default  */
+			caddr_t	tagbuf;	   /* tag data buffer (0=default)  */
+		} f;
+		struct swim_drvstatus status;
+	} p;
+};
+
+struct swimcmd_fmtverify {
+	__u8	code;		/* CMD_FORMAT_VERIFY */
+	__u8	drive_num;
+	__u16	error;
+};
+
+struct swimcmd_rw {
+	__u8	code;		/* CMD_READ, CMD_WRITE or CMD_READ_VERIFY */
+	__u8	drive_num;
+	__u16	error;
+	caddr_t	buffer;		/* R/W buffer address */
+	__u32	first_block;	/* Starting block     */
+	__u32	num_blocks;	/* Number of blocks   */
+	__u8	tag[12];	/* tag data           */
+};
+
+struct swimcmd_cachectl {
+	__u8	code;		/* CMD_CACHE_CTRL */
+	__u8	unused;
+	__u16	error;
+	__u8	enable;		/* Nonzero to enable cache                */
+	__u8	install;	/* +1 = install, -1 = remove, 0 = neither */
+};
+
+struct swimcmd_tagbufctl {
+	__u8	code;		/* CMD_TAGBUFF_CTRL */
+	__u8	unused;
+	__u16	error;
+	caddr_t	buf;		/* buffer address or 0 to disable */
+};
+
+struct swimcmd_geticon {
+	__u8	code;		/* CMD_GET_ICON */
+	__u8	drive_num;
+	__u16	error;
+	caddr_t	buffer;		/* Nuffer address */
+	__u16	kind;		/* 0 = media icon, 1 = drive icon */
+	__u16	unused;
+	__u16	max_bytes;	/* maximum  byte count */
+};
+
+/* Messages from the SWIM IOP to the host CPU: */
+
+struct swimmsg_status {
+	__u8	code;		/* 1 = insert, 2 = eject, 3 = status changed */
+	__u8	drive_num;
+	__u16	error;
+	struct swim_drvstatus status;
+};
+
+#endif /* __ASSEMBLY__ */
diff -urN linux-2.2.18-pre15/include/asm-m68k/adb.h linux/include/asm-m68k/adb.h
--- linux-2.2.18-pre15/include/asm-m68k/adb.h	Fri May  8 00:15:22 1998
+++ linux/include/asm-m68k/adb.h	Mon Dec  3 20:34:13 2001
@@ -58,7 +58,7 @@
     int reply_len;
     unsigned char reply_expected;
     unsigned char sent;
-    unsigned char got_reply;
+    unsigned char complete;
     void (*done)(struct adb_request *);
     void *arg;
     struct adb_request *next;
diff -urN linux-2.2.18-pre15/include/asm-m68k/bootinfo.h linux/include/asm-m68k/bootinfo.h
--- linux-2.2.18-pre15/include/asm-m68k/bootinfo.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/bootinfo.h	Mon Dec  3 20:34:13 2001
@@ -103,6 +103,30 @@
 #define ATARI_MACH_AB40		3	/* Afterburner040 on Falcon */
 
     /*
+     *  VME-specific tags
+     */
+
+#define BI_VME_TYPE		0x8000	/* VME sub-architecture (u_long) */
+#define BI_VME_BRDINFO		0x8001	/* VME board information (struct) */
+
+/* BI_VME_TYPE codes */
+#define	VME_TYPE_TP34V		0x0034	/* Tadpole TP34V */
+#define VME_TYPE_MVME147	0x0147	/* Motorola MVME147 */
+#define VME_TYPE_MVME162	0x0162	/* Motorola MVME162 */
+#define VME_TYPE_MVME166	0x0166	/* Motorola MVME166 */
+#define VME_TYPE_MVME167	0x0167	/* Motorola MVME167 */
+#define VME_TYPE_MVME172	0x0172	/* Motorola MVME172 */
+#define VME_TYPE_MVME177	0x0177	/* Motorola MVME177 */
+#define VME_TYPE_BVME4000	0x4000	/* BVM Ltd. BVME4000 */
+#define VME_TYPE_BVME6000	0x6000	/* BVM Ltd. BVME6000 */
+
+/* BI_VME_BRDINFO is a 32 byte struct as returned by the Bug code on
+ * Motorola VME boards.  Contains board number, Bug version, board
+ * configuration options, etc.  See include/asm/mvme16xhw.h for details.
+ */
+
+
+    /*
      *  Macintosh-specific tags (all u_long)
      */
 
diff -urN linux-2.2.18-pre15/include/asm-m68k/fpu.h linux/include/asm-m68k/fpu.h
--- linux-2.2.18-pre15/include/asm-m68k/fpu.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-m68k/fpu.h	Mon Dec  3 20:34:13 2001
@@ -11,6 +11,8 @@
 #define FPSTATESIZE (216/sizeof(unsigned char))
 #elif defined(CONFIG_M68040)
 #define FPSTATESIZE (96/sizeof(unsigned char))
+#elif defined(CONFIG_FPU_EMU)
+#define FPSTATESIZE (28/sizeof(unsigned char))
 #elif defined(CONFIG_M68060)
 #define FPSTATESIZE (12/sizeof(unsigned char))
 #else
diff -urN linux-2.2.18-pre15/include/asm-m68k/ide.h linux/include/asm-m68k/ide.h
--- linux-2.2.18-pre15/include/asm-m68k/ide.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/ide.h	Mon Dec  3 20:34:13 2001
@@ -91,10 +91,6 @@
 	} b;
 	} select_t;
 
-#ifdef CONFIG_MAC	/* MSch: Hack; wrapper for ide_intr */
-void mac_ide_intr(int irq, void *dev_id, struct pt_regs *regs);
-#endif
-
 static __inline__ int ide_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 			unsigned long flags, const char *device, void *dev_id)
 {
@@ -108,11 +104,7 @@
 #endif /* CONFIG_Q40*/
 #ifdef CONFIG_MAC
 	if (MACH_IS_MAC)
-#if 0	/* MSch Hack: maybe later we'll call ide_intr without a wrapper */
-	return nubus_request_irq(12, dev_id, handler);
-#else
-	return nubus_request_irq(12, dev_id, mac_ide_intr);
-#endif
+		return request_irq(irq, handler, 0, device, dev_id);
 #endif /* CONFIG_MAC */
 	return 0;
 }
@@ -129,7 +121,7 @@
 #endif /* CONFIG_Q40*/
 #ifdef CONFIG_MAC
 	if (MACH_IS_MAC)
-		nubus_free_irq(12);
+		free_irq(irq, dev_id);
 #endif /* CONFIG_MAC */
 }
 
diff -urN linux-2.2.18-pre15/include/asm-m68k/init.h linux/include/asm-m68k/init.h
--- linux-2.2.18-pre15/include/asm-m68k/init.h	Tue Jan 19 10:58:34 1999
+++ linux/include/asm-m68k/init.h	Mon Dec  3 20:34:13 2001
@@ -3,7 +3,7 @@
 
 #include <linux/config.h>
 
-#ifndef CONFIG_KGDB
+#if !defined(CONFIG_KGDB) && !defined(CONFIG_FTRACE)
 
 #define __init __attribute__ ((__section__ (".text.init")))
 #define __initdata __attribute__ ((__section__ (".data.init")))
diff -urN linux-2.2.18-pre15/include/asm-m68k/irq.h linux/include/asm-m68k/irq.h
--- linux-2.2.18-pre15/include/asm-m68k/irq.h	Fri Apr 17 22:04:44 1998
+++ linux/include/asm-m68k/irq.h	Mon Dec  3 20:34:13 2001
@@ -12,14 +12,19 @@
 /*
  * This should be the same as the max(NUM_X_SOURCES) for all the
  * different m68k hosts compiled into the kernel.
- * Currently the Atari has 72 and the Amiga 24, but if both are
- * supported in the kernel it is better to make room for 72.
+ * Currently the Atari has 72, Mac has 64 and the Amiga 24; this
+ * makes sure there is room for the largest table needed by the
+ * architectures supported by the kernel.
  */
 #if defined(CONFIG_ATARI)
 #define NR_IRQS (72+SYS_IRQS)
 #else
+#if defined(CONFIG_MAC)
+#define NR_IRQS (64+SYS_IRQS)
+#else
 #define NR_IRQS (24+SYS_IRQS)
 #endif
+#endif
 
 /*
  * Interrupt source definitions
@@ -69,6 +74,7 @@
 
 extern void (*enable_irq)(unsigned int);
 extern void (*disable_irq)(unsigned int);
+#define disable_irq_nosync(i) disable_irq(i)
 
 extern int sys_request_irq(unsigned int, 
 	void (*)(int, void *, struct pt_regs *), 
diff -urN linux-2.2.18-pre15/include/asm-m68k/keyboard.h linux/include/asm-m68k/keyboard.h
--- linux-2.2.18-pre15/include/asm-m68k/keyboard.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/keyboard.h	Mon Dec  3 20:34:13 2001
@@ -19,6 +19,10 @@
 #ifdef CONFIG_Q40
 #include <asm/q40_keyboard.h>
 #endif
+#ifdef CONFIG_MAC
+extern int mackbd_translate(unsigned char, unsigned char *, char);
+extern int mackbd_unexpected_up(unsigned char);
+#endif
 
 static __inline__ int kbd_setkeycode(unsigned int scancode,
 				     unsigned int keycode)
@@ -39,6 +43,15 @@
     return scancode > 127 ? -EINVAL : scancode;
 }
 
+static __inline__ int kbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+#ifdef CONFIG_Q40
+    if (MACH_IS_Q40)
+        return q40kbd_pretranslate(scancode,raw_mode);
+#endif
+    return 1;
+}
+
 static __inline__ int kbd_translate(unsigned char scancode,
 				    unsigned char *keycode, char raw_mode)
 {
@@ -46,6 +59,10 @@
     if (MACH_IS_Q40)
         return q40kbd_translate(scancode,keycode,raw_mode);
 #endif
+#ifdef CONFIG_MAC
+    if (MACH_IS_MAC)
+        return mackbd_translate(scancode, keycode, raw_mode);
+#endif
     *keycode = scancode;
     return 1;
 }
@@ -55,6 +72,10 @@
 #ifdef CONFIG_Q40
     if (MACH_IS_Q40)
         return q40kbd_unexpected_up(keycode);
+#endif
+#ifdef CONFIG_MAC
+    if (MACH_IS_MAC)
+        return mackbd_unexpected_up(keycode);
 #endif
     return 0200;
 }
diff -urN linux-2.2.18-pre15/include/asm-m68k/linux_logo.h linux/include/asm-m68k/linux_logo.h
--- linux-2.2.18-pre15/include/asm-m68k/linux_logo.h	Mon Oct  5 13:54:39 1998
+++ linux/include/asm-m68k/linux_logo.h	Mon Dec  3 20:34:13 2001
@@ -890,6 +890,7 @@
 
 #define INCLUDE_LINUX_LOGOBW
 #define INCLUDE_LINUX_LOGO16
+#define INCLUDE_LINUX_LOGO4
 #include <linux/linux_logo.h>
 
 #else
@@ -904,5 +905,9 @@
 extern unsigned char linux_logo16_green[];
 extern unsigned char linux_logo16_blue[];
 extern unsigned char linux_logo16[];
+extern unsigned char linux_logo4_red[];
+extern unsigned char linux_logo4_green[];
+extern unsigned char linux_logo4_blue[];
+extern unsigned char linux_logo4[];
 
 #endif
diff -urN linux-2.2.18-pre15/include/asm-m68k/mac_psc.h linux/include/asm-m68k/mac_psc.h
--- linux-2.2.18-pre15/include/asm-m68k/mac_psc.h	Mon Oct  5 13:54:39 1998
+++ linux/include/asm-m68k/mac_psc.h	Mon Dec  3 20:34:13 2001
@@ -1,73 +1,243 @@
 /*
- *	Apple Peripheral System Controller (PSC)
+ * Apple Peripheral System Controller (PSC)
  *
- *	The PSC is used on the AV Macs to control IO functions not handled
- *	by the VIAs (Ethernet, DSP, SCC).
+ * The PSC is used on the AV Macs to control IO functions not handled
+ * by the VIAs (Ethernet, DSP, SCC, Sound). This includes nine DMA
+ * channels.
+ *
+ * The first seven DMA channels appear to be "one-shot" and are actually
+ * sets of two channels; one member is active while the other is being
+ * configured, and then you flip the active member and start all over again.
+ * The one-shot channels are grouped together and are:
+ *
+ * 1. SCSI
+ * 2. Ethernet Read
+ * 3. Ethernet Write
+ * 4. Floppy Disk Controller
+ * 5. SCC Channel A Receive
+ * 6. SCC Channel B Receive
+ * 7. SCC Channel A Transmit
+ *
+ * The remaining two channels are handled somewhat differently. They appear
+ * to be closely tied and share one set of registers. They also seem to run
+ * continuously, although how you keep the buffer filled in this scenario is
+ * not understood as there seems to be only one input and one output buffer
+ * pointer.
+ * 
+ * Much of this was extrapolated from what was known about the Ethernet
+ * registers and subsequently confirmed using MacsBug (ie by pinging the
+ * machine with easy-to-find patterns and looking for them in the DMA
+ * buffers, or by sending a file over the serial ports and finding the
+ * file in the buffers.)
+ *
+ * 1999-05-25 (jmt)
  */
- 
-#define PSCBASE		0x50F31000
+
+#define PSC_BASE	(0x50F31000)
 
 /*
- *	The IER/IFR registers work like the VIA, except that it has 4
- *	of them each on different interrupt levels.
+ * The IER/IFR registers work like the VIA, except that it has 4
+ * of them each on different interrupt levels, and each register
+ * set only seems to handle four interrupts instead of seven.
+ *
+ * To access a particular set of registers, add 0xn0 to the base
+ * where n = 3,4,5 or 6.
  */
  
-#define pIFR3		0x130
-#define pIFR4		0x140
-#define pIFR5		0x150
-#define pIFR6		0x160
-
-#define pIER3		0x134
-#define pIER4		0x144
-#define pIER5		0x154
-#define pIER6		0x164
+#define pIFRbase	0x100
+#define pIERbase	0x104
 
 /*
- *	Ethernet Control Registers
+ * One-shot DMA control registers
  */
- 
-#define PSC_ENETRD_CTL  0xc10
-#define PSC_ENETWR_CTL  0xc20
+
+#define PSC_CTL_BASE	0xC00
+
+#define PSC_SCSI_CTL	0xC00
+#define PSC_ENETRD_CTL  0xC10
+#define PSC_ENETWR_CTL  0xC20
+#define PSC_FDC_CTL	0xC30
+#define PSC_SCCA_CTL	0xC40
+#define PSC_SCCB_CTL	0xC50
+#define PSC_SCCATX_CTL	0xC60
+
+/*
+ * DMA channels. Add +0x10 for the second channel in the set.
+ * You're supposed to use one channel while the other runs and
+ * then flip channels and do the whole thing again.
+ */
+
+#define PSC_ADDR_BASE	0x1000
+#define PSC_LEN_BASE	0x1004
+#define PSC_CMD_BASE	0x1008
+
+#define PSC_SCSI_ADDR	0x1000 	/* confirmed */
+#define PSC_SCSI_LEN	0x1004 	/* confirmed */
+#define PSC_SCSI_CMD	0x1008 	/* confirmed */
+#define PSC_ENETRD_ADDR 0x1020	/* confirmed */
+#define PSC_ENETRD_LEN  0x1024	/* confirmed */
+#define PSC_ENETRD_CMD  0x1028	/* confirmed */
+#define PSC_ENETWR_ADDR 0x1040	/* confirmed */
+#define PSC_ENETWR_LEN  0x1044	/* confirmed */
+#define PSC_ENETWR_CMD  0x1048	/* confirmed */
+#define PSC_FDC_ADDR	0x1060	/* strongly suspected */
+#define PSC_FDC_LEN	0x1064	/* strongly suspected */
+#define PSC_FDC_CMD	0x1068	/* strongly suspected */
+#define PSC_SCCA_ADDR	0x1080	/* confirmed */
+#define PSC_SCCA_LEN	0x1084	/* confirmed */
+#define PSC_SCCA_CMD	0x1088	/* confirmed */
+#define PSC_SCCB_ADDR	0x10A0	/* confirmed */
+#define PSC_SCCB_LEN	0x10A4	/* confirmed */
+#define PSC_SCCB_CMD	0x10A8	/* confirmed */
+#define PSC_SCCATX_ADDR	0x10C0	/* confirmed */
+#define PSC_SCCATX_LEN	0x10C4	/* confirmed */
+#define PSC_SCCATX_CMD	0x10C8	/* confirmed */
 
 /*
- *	Receive DMA channel (add +0x10 for 2nd channel)
+ * Free-running DMA registers. The only part known for sure are the bits in
+ * the control register, the buffer addresses and the buffer length. Everything
+ * else is anybody's guess.
+ *
+ * These registers seem to be mirrored every thirty-two bytes up until offset
+ * 0x300. It's safe to assume then that a new set of registers starts there.
  */
- 
-#define PSC_ENETRD_ADDR 0x1020 
-#define PSC_ENETRD_LEN  0x1024
-#define PSC_ENETRD_CMD  0x1028
+
+#define PSC_SND_CTL	0x200	/*
+				 * [ 16-bit ]
+				 * Sound (Singer?) control register.
+				 *
+				 * bit 0  : ????
+				 * bit 1  : ????
+				 * bit 2  : Set to one to enable sound
+				 *          output. Possibly a mute flag.
+				 * bit 3  : ????
+				 * bit 4  : ????
+				 * bit 5  : ????
+				 * bit 6  : Set to one to enable pass-thru
+				 *          audio. In this mode the audio data
+				 *          seems to appear in both the input
+				 *          buffer and the output buffer.
+				 * bit 7  : Set to one to activate the
+				 *          sound input DMA or zero to
+				 *          disable it.
+				 * bit 8  : Set to one to activate the
+				 *          sound output DMA or zero to
+				 *          disable it.
+				 * bit 9  : \
+				 * bit 11 :  |
+				 *          These two bits control the sample
+				 *          rate. Usually set to binary 10 and
+				 *	    MacOS 8.0 says I'm at 48 KHz. Using
+				 *	    a binary value of 01 makes things
+				 *	    sound about 1/2 speed (24 KHz?) and
+				 *          binary 00 is slower still (22 KHz?)
+				 *
+				 * Setting this to 0x0000 is a good way to
+				 * kill all DMA at boot time so that the
+				 * PSC won't overwrite the kernel image
+				 * with sound data.
+				 */
 
 /*
- *	Transmit DMA channel (add +0x10 for 2nd channel)
+ * 0x0202 - 0x0203 is unused. Writing there
+ * seems to clobber the control register.
  */
- 
-#define PSC_ENETWR_ADDR 0x1040
-#define PSC_ENETWR_LEN  0x1044
-#define PSC_ENETWR_CMD  0x1048
+
+#define PSC_SND_SOURCE	0x204	/*
+				 * [ 32-bit ]
+				 * Controls input source and volume:
+				 *
+				 * bits 12-15 : input source volume, 0 - F
+				 * bits 16-19 : unknown, always 0x5
+				 * bits 20-23 : input source selection:
+				 *                  0x3 = CD Audio
+				 *                  0x4 = External Audio
+				 *
+				 * The volume is definately not the general
+				 * output volume as it doesn't affect the
+				 * alert sound volume.
+				 */
+#define PSC_SND_STATUS1	0x208	/*
+				 * [ 32-bit ]
+				 * Appears to be a read-only status register.
+				 * The usual value is 0x00400002.
+				 */
+#define PSC_SND_HUH3	0x20C	/*
+				 * [ 16-bit ]
+				 * Unknown 16-bit value, always 0x0000.
+				 */
+#define PSC_SND_BITS2GO	0x20E	/*
+				 * [ 16-bit ]
+				 * Counts down to zero from some constant
+				 * value. The value appears to be the
+				 * number of _bits_ remaining before the
+				 * buffer is full, which would make sense
+				 * since Apple's docs say the sound DMA
+				 * channels are 1 bit wide.
+				 */
+#define PSC_SND_INADDR	0x210	/*
+				 * [ 32-bit ]
+				 * Address of the sound input DMA buffer
+				 */
+#define PSC_SND_OUTADDR	0x214	/*
+				 * [ 32-bit ]
+				 * Address of the sound output DMA buffer
+				 */
+#define PSC_SND_LEN	0x218	/*
+				 * [ 16-bit ]
+				 * Length of both buffers in eight-byte units.
+				 */
+#define PSC_SND_HUH4	0x21A	/*
+				 * [ 16-bit ]
+				 * Unknown, always 0x0000.
+				 */
+#define PSC_SND_STATUS2	0x21C	/*
+				 * [ 16-bit ]
+				 * Appears to e a read-only status register.
+				 * The usual value is 0x0200.
+				 */
+#define PSC_SND_HUH5	0x21E	/*
+				 * [ 16-bit ]
+				 * Unknown, always 0x0000.
+				 */
+
+#ifndef __ASSEMBLY__
+
+extern volatile __u8 *psc;
+extern int psc_present;
 
 /*
  *	Access functions
  */
  
-extern volatile unsigned char *psc;
+extern inline void psc_write_byte(int offset, __u8 data)
+{
+	*((volatile __u8 *)(psc + offset)) = data;
+}
+
+extern inline void psc_write_word(int offset, __u16 data)
+{
+	*((volatile __u16 *)(psc + offset)) = data;
+}
 
-extern inline void psc_write_word(int offset, u16 data)
+extern inline void psc_write_long(int offset, __u32 data)
 {
-	*((volatile u16 *)(psc+offset)) = data;
+	*((volatile __u32 *)(psc + offset)) = data;
 }
 
-extern inline void psc_write_long(int offset, u32 data)
+extern inline u8 psc_read_byte(int offset)
 {
-	*((volatile u32 *)(psc+offset)) = data;
+	return *((volatile __u8 *)(psc + offset));
 }
 
 extern inline u16 psc_read_word(int offset)
 {
-	return *((volatile u16 *)(psc+offset));
+	return *((volatile __u16 *)(psc + offset));
 }
 
 extern inline u32 psc_read_long(int offset)
 {
-	return *((volatile u32 *)(psc+offset));
+	return *((volatile __u32 *)(psc + offset));
 }
 
+#endif /* __ASSEMBLY__ */
diff -urN linux-2.2.18-pre15/include/asm-m68k/machdep.h linux/include/asm-m68k/machdep.h
--- linux-2.2.18-pre15/include/asm-m68k/machdep.h	Tue Jan 19 10:58:34 1999
+++ linux/include/asm-m68k/machdep.h	Mon Dec  3 20:34:13 2001
@@ -30,6 +30,8 @@
 extern int (*mach_hwclk)(int, struct hwclk_time*);
 extern int (*mach_set_clock_mmss)(unsigned long);
 extern void (*mach_reset)( void );
+extern void (*mach_halt)( void );
+extern void (*mach_power_off)( void );
 extern unsigned long (*mach_hd_init) (unsigned long, unsigned long);
 extern void (*mach_hd_setup)(char *, int *);
 extern long mach_max_dma_address;
diff -urN linux-2.2.18-pre15/include/asm-m68k/machw.h linux/include/asm-m68k/machw.h
--- linux-2.2.18-pre15/include/asm-m68k/machw.h	Tue Jan 19 10:58:34 1999
+++ linux/include/asm-m68k/machw.h	Mon Dec  3 20:34:13 2001
@@ -25,6 +25,7 @@
 
 #include <linux/types.h>
 
+#if 0
 /* Mac SCSI Controller 5380 */
 
 #define	MAC_5380_BAS	(0x50F10000) /* This is definitely wrong!! */
@@ -63,65 +64,7 @@
   u_char cha_b_data;
  };
 # define mac_scc ((*(volatile struct SCC*)MAC_SCC_BAS))
-
-/*
-** VIA 6522
-*/
-
-#define VIA1_BAS	(0x50F00000)
-#define VIA2_BAS	(0x50F02000)
-#define VIA2_BAS_IIci	(0x50F26000)
-struct VIA
- {
-  u_char buf_b;
-  u_char dummy1[0x199];
-  u_char buf_a;
-  u_char dummy2[0x199];
-  u_char dir_b;
-  u_char dummy3[0x199];
-  u_char dir_a;
-  u_char dummy4[0x199];
-  u_char timer1_cl;
-  u_char dummy5[0x199];
-  u_char timer1_ch;
-  u_char dummy6[0x199];
-  u_char timer1_ll;
-  u_char dummy7[0x199];
-  u_char timer1_lh;
-  u_char dummy8[0x199];
-  u_char timer2_cl;
-  u_char dummy9[0x199];
-  u_char timer2_ch;
-  u_char dummy10[0x199];
-  u_char sr;
-  u_char dummy11[0x199];
-  u_char acr;
-  u_char dummy12[0x199];
-  u_char pcr;
-  u_char dummy13[0x199];
-  u_char int_fl;
-  u_char dummy14[0x199];
-  u_char int_en;
-  u_char dummy15[0x199];
-  u_char anr;
-  u_char dummy16[0x199];
- };
-
-# define via_1         ((*(volatile struct VIA *)VIA1_BAS))
-# define via_2         ((*(volatile struct VIA *)VIA2_BAS))
-# define via1_regp     ((volatile unsigned char *)VIA1_BAS)
- 
-/*
- * OSS/RBV base address 
- */
-
-#define OSS_BAS		0x50f1a000
-#define PSC_BAS		0x50f31000
-
-/* move to oss.h?? */
-#define nIFR	0x203
-#define oIFR	0x202
-
+#endif
 
 /* hardware stuff */
 
diff -urN linux-2.2.18-pre15/include/asm-m68k/macintosh.h linux/include/asm-m68k/macintosh.h
--- linux-2.2.18-pre15/include/asm-m68k/macintosh.h	Mon Oct  5 13:54:39 1998
+++ linux/include/asm-m68k/macintosh.h	Mon Dec  3 20:34:13 2001
@@ -6,6 +6,7 @@
  */
  
 extern void mac_reset(void);
+extern void mac_poweroff(void);
 extern void mac_init_IRQ(void);
 extern int mac_request_irq (unsigned int, void (*)(int, void *, 
 				struct pt_regs *),
@@ -21,7 +22,6 @@
 extern void mac_report_hardware(void);
 extern void mac_debugging_penguin(int);
 extern void mac_boom(int);
-extern void mac_video_setup(char *,int *);
 
 /*
  *	Floppy driver magic hook - probably shouldnt be here
@@ -58,6 +58,7 @@
 #define MAC_ADB_CUDA		3
 #define MAC_ADB_PB1		4
 #define MAC_ADB_PB2		5
+#define MAC_ADB_IOP		6
 
 #define MAC_VIA_II		1
 #define MAC_VIA_IIci		2
@@ -131,17 +132,18 @@
 #define MAC_MODEL_P550		80	/* aka: LC550, P560 */
 #define MAC_MODEL_CCLII		83	/* aka: P275 */
 #define MAC_MODEL_PB165		84
+#define MAC_MODEL_PB190		85	/* aka: PB190CS */
 #define MAC_MODEL_TV		88
 #define MAC_MODEL_P475		89	/* aka: LC475, P476 */
 #define MAC_MODEL_P475F		90	/* aka: P475 w/ FPU (no LC040) */
 #define MAC_MODEL_P575		92	/* aka: LC575, P577/P578 */
 #define MAC_MODEL_Q605		94
+#define MAC_MODEL_Q605_ACC	95	/* Quadra 605 accelerated to 33MHz  */
 #define MAC_MODEL_Q630		98	/* aka: LC630, P630/631/635/636/637/638/640 */
 #define MAC_MODEL_P588		99	/* aka: LC580, P580 */
 #define MAC_MODEL_PB280		102
 #define MAC_MODEL_PB280C	103
 #define MAC_MODEL_PB150		115
-#define MAC_MODEL_PB190		122	/* aka: PB190CS */
 
 extern struct mac_model *macintosh_config;
  
diff -urN linux-2.2.18-pre15/include/asm-m68k/macints.h linux/include/asm-m68k/macints.h
--- linux-2.2.18-pre15/include/asm-m68k/macints.h	Sat Jun 13 13:14:33 1998
+++ linux/include/asm-m68k/macints.h	Mon Dec  3 20:34:13 2001
@@ -14,38 +14,49 @@
 
 #include <asm/irq.h>
 
-/*
-** Macintosh Interrupt sources.
-**
-** Note: these are all routed via the generic VIA interrupt routine!
-**
-*/
+/* Setting this prints debugging info for unclaimed interrupts */
+
+#define DEBUG_SPURIOUS
+
+/* Setting this prints debugging info on each autovector interrupt */
+
+/* #define DEBUG_IRQS */
+
+/* Setting this prints debugging info on each Nubus interrupt */
 
-#define SRC_VIA1		0
-#define SRC_VIA2		1
+/* #define DEBUG_NUBUS_INT */
+
+/* Setting this prints debugging info on irqs as they enabled and disabled. */
+
+/* #define DEBUG_IRQUSE */
+
+/*
+ * Base IRQ number for all Mac68K interrupt sources. Each source
+ * has eight indexes (base -> base+7).
+ */
 
 #define VIA1_SOURCE_BASE	8
 #define VIA2_SOURCE_BASE	16
-#define RBV_SOURCE_BASE		24
-#define MAC_SCC_SOURCE_BASE	32
+#define MAC_SCC_SOURCE_BASE	24
+#define PSC3_SOURCE_BASE	24
+#define PSC4_SOURCE_BASE	32
+#define PSC5_SOURCE_BASE	40
+#define PSC6_SOURCE_BASE	48
 #define NUBUS_SOURCE_BASE	56
-#define NUBUS_MAX_SOURCES	8
 
-/* FIXME: sources not contigous ... */
-#define NUM_MAC_SOURCES   (NUBUS_SOURCE_BASE+NUBUS_MAX_SOURCES-VIA1_SOURCE_BASE)
+/*
+ * Maximum IRQ number is NUBUS_SOURCE_BASE + 7,
+ * giving us IRQs up through 63.
+ */
 
-#define IRQ_SRC_MASK (VIA1_SOURCE_BASE|VIA2_SOURCE_BASE|MAC_SCC_SOURCE_BASE)
-#define IRQ_IDX_MASK 7
+#define NUM_MAC_SOURCES		64
 
 /* 
- * quick hack to adapt old MACHSPEC-aware source
+ * clean way to separate IRQ into its source and index
  */
-#define	IRQ_IDX(irq)	(irq)
 
-/* interrupt service types */
-#define IRQ_TYPE_SLOW     0
-#define IRQ_TYPE_FAST     1
-#define IRQ_TYPE_PRIO     2
+#define IRQ_SRC(irq)	(irq >> 3)
+#define	IRQ_IDX(irq)	(irq & 7)
 
 #define	IRQ_SPURIOUS      (0)
 
@@ -59,15 +70,15 @@
 #define IRQ_AUTO_7        (7)
 
 /* VIA1 interrupts */
-#define IRQ_VIA1_0	  (8)	/* one second int. */
-#define IRQ_VIA1_1        (9)	/* VBlank int. */
+#define IRQ_VIA1_0	  (8)		/* one second int. */
+#define IRQ_VIA1_1        (9)		/* VBlank int. */
 #define IRQ_MAC_VBL	  IRQ_VIA1_1
-#define IRQ_VIA1_2 	  (10)	/* ADB SR shifts complete */
+#define IRQ_VIA1_2 	  (10)		/* ADB SR shifts complete */
 #define IRQ_MAC_ADB	  IRQ_VIA1_2
 #define IRQ_MAC_ADB_SR	  IRQ_VIA1_2
-#define IRQ_VIA1_3	  (11)	/* ADB SR CB2 ?? */
+#define IRQ_VIA1_3	  (11)		/* ADB SR CB2 ?? */
 #define IRQ_MAC_ADB_SD	  IRQ_VIA1_3
-#define IRQ_VIA1_4        (12)	/* ADB SR ext. clock pulse */
+#define IRQ_VIA1_4        (12)		/* ADB SR ext. clock pulse */
 #define IRQ_MAC_ADB_CL	  IRQ_VIA1_4
 #define IRQ_VIA1_5	  (13)
 #define IRQ_MAC_TIMER_2	  IRQ_VIA1_5
@@ -75,7 +86,7 @@
 #define IRQ_MAC_TIMER_1	  IRQ_VIA1_6
 #define IRQ_VIA1_7        (15)
 
-/* VIA2 interrupts */
+/* VIA2/RBV interrupts */
 #define IRQ_VIA2_0	  (16)
 #define IRQ_MAC_SCSIDRQ	  IRQ_VIA2_0
 #define IRQ_VIA2_1        (17)
@@ -88,18 +99,6 @@
 #define IRQ_VIA2_6	  (22)
 #define IRQ_VIA2_7        (23)
 
-#if 0
-/* RBV interrupts */
-#define IRQ_RBV_0	  (24)
-#define IRQ_RBV_1	  (25)
-#define IRQ_RBV_2 	  (26)
-#define IRQ_RBV_3	  (27)
-#define IRQ_RBV_4	  (28)
-#define IRQ_RBV_5	  (29)
-#define IRQ_RBV_6	  (30)
-#define IRQ_RBV_7	  (31)
-#endif
-
 /* Level 3 (PSC, AV Macs only) interrupts */
 #define IRQ_PSC3_0	  (24)
 #define IRQ_MAC_MACE	  IRQ_PSC3_0
@@ -109,8 +108,8 @@
 
 /* Level 4 (SCC) interrupts */
 #define IRQ_SCC 	     (32)
-#define IRQ_SCCB	     (33)
-#define IRQ_SCCA	     (34)
+#define IRQ_SCCA	     (33)
+#define IRQ_SCCB	     (34)
 #if 0 /* FIXME: are there multiple interrupt conditions on the SCC ?? */
 /* SCC interrupts */
 #define IRQ_SCCB_TX	     (32)
@@ -143,40 +142,21 @@
 #define IRQ_PSC6_3	  (51)
 
 /* Nubus interrupts (cascaded to VIA2) */
-#define IRQ_NUBUS_1	  (56)
+#define IRQ_NUBUS_9	  (56)
+#define IRQ_NUBUS_A	  (57)
+#define IRQ_NUBUS_B	  (58)
+#define IRQ_NUBUS_C	  (59)
+#define IRQ_NUBUS_D	  (60)
+#define IRQ_NUBUS_E	  (61)
+#define IRQ_NUBUS_F	  (62)
+
+#define SLOT2IRQ(x)	  (x + 47)
+#define IRQ2SLOT(x)	  (x - 47)
 
 #define INT_CLK   24576	    /* CLK while int_clk =2.456MHz and divide = 100 */
 #define INT_TICKS 246	    /* to make sched_time = 99.902... HZ */
 
-
-#define VIA_ENABLE	0
-#define VIA_PENDING	1
-#define VIA_SERVICE	2
-#define VIA_MASK	3
-
-/* 
- * Utility functions for setting/clearing bits in the interrupt registers of
- * the VIA. 
- */
-
-void mac_enable_irq( unsigned irq );
-void mac_disable_irq( unsigned irq );
-void mac_turnon_irq( unsigned irq );
-void mac_turnoff_irq( unsigned irq );
-void mac_clear_pending_irq( unsigned irq );
-int  mac_irq_pending( unsigned irq );
-int  nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *));
-int  nubus_free_irq(int slot);
-
-unsigned long mac_register_nubus_int( void );
-void mac_unregister_nubus_int( unsigned long );
-
-extern void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs);
-extern void via1_irq(int irq, void *dev_id, struct pt_regs *regs);
-extern void via2_irq(int irq, void *dev_id, struct pt_regs *regs);
-extern void  rbv_irq(int irq, void *dev_id, struct pt_regs *regs);
-extern void mac_bang(int irq, void *dev_id, struct pt_regs *regs);
-		
-extern void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs);
+extern irq_node_t *mac_irq_list[NUM_MAC_SOURCES];
+extern void mac_do_irq_list(int irq, struct pt_regs *);
 
 #endif /* asm/macints.h */
diff -urN linux-2.2.18-pre15/include/asm-m68k/page.h linux/include/asm-m68k/page.h
--- linux-2.2.18-pre15/include/asm-m68k/page.h	Mon Dec  3 20:33:36 2001
+++ linux/include/asm-m68k/page.h	Mon Dec  3 20:34:13 2001
@@ -11,6 +11,7 @@
 #ifdef __KERNEL__
 
 #include <asm/setup.h>
+#include <asm/bootinfo.h>
 
 #define STRICT_MM_TYPECHECKS
 
@@ -124,9 +125,14 @@
 #ifdef CONFIG_AMIGA
 	if (MACH_IS_AMIGA && (physaddr < 16*1024*1024))
 		return (void *)0xffffffff;
-	else
 #endif
-		return (void *)(physaddr+PAGE_OFFSET);
+#ifdef CONFIG_MAC
+	if (MACH_IS_MAC && (physaddr >= mac_bi_data.videoaddr) &&
+	    (physaddr < mac_bi_data.videoaddr +
+	     (mac_bi_data.videorow * (mac_bi_data.dimensions>>16))))
+		return (void *)0xffffffff;
+#endif
+	return (void *)(physaddr+PAGE_OFFSET);
 }
 #define MAP_NR(addr)		(__pa(addr) >> PAGE_SHIFT)
 
diff -urN linux-2.2.18-pre15/include/asm-m68k/setup.h linux/include/asm-m68k/setup.h
--- linux-2.2.18-pre15/include/asm-m68k/setup.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/setup.h	Mon Dec  3 20:34:13 2001
@@ -244,6 +244,9 @@
 extern unsigned long m68k_cputype;
 extern unsigned long m68k_fputype;
 extern unsigned long m68k_mmutype;			/* Not really used yet */
+#ifdef CONFIG_VME
+extern unsigned long vme_brdtype;
+#endif
 
     /*
      *  m68k_is040or060 is != 0 for a '040 or higher;
@@ -314,6 +317,16 @@
 #endif
 
 #define CPU_TYPE (m68k_cputype)
+
+#ifdef CONFIG_FPU_EMU
+#  ifdef CONFIG_FPU_EMU_ONLY
+#    define FPU_IS_EMU (1)
+#  else
+#    define FPU_IS_EMU (!m68k_fputype)
+#  endif
+#else
+#  define FPU_IS_EMU (0)
+#endif
 
 
     /*
diff -urN linux-2.2.18-pre15/include/asm-m68k/system.h linux/include/asm-m68k/system.h
--- linux-2.2.18-pre15/include/asm-m68k/system.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/system.h	Mon Dec  3 20:34:13 2001
@@ -4,6 +4,7 @@
 #include <linux/config.h> /* get configuration macros */
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#include <asm/hardirq.h>
 
 extern inline unsigned long rdusp(void) {
   	unsigned long usp;
@@ -60,11 +61,17 @@
 #define __xg(x) ((volatile struct __xchg_dummy *)(x))
 
 #if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
-/* block out HSYNC on the atari */
-#define __sti() __asm__ __volatile__ ("andiw #0xfbff,%/sr": : : "memory")
+#define __sti() ({ \
+	if (!local_irq_count[smp_processor_id()]) \
+		/* block out HSYNC on the atari */ \
+		__asm__ __volatile__ ("andiw #0xfbff,%/sr": : : "memory"); \
+})
 #else /* portable version */
-#define __sti() __asm__ __volatile__ ("andiw #0xf8ff,%/sr": : : "memory")
-#endif /* machine compilation types */ 
+#define __sti() ({ \
+	if (!local_irq_count[smp_processor_id()]) \
+		__asm__ __volatile__ ("andiw #0xf8ff,%/sr": : : "memory"); \
+})
+#endif /* machine compilation types */
 #define __cli() __asm__ __volatile__ ("oriw  #0x0700,%/sr": : : "memory")
 #define nop() __asm__ __volatile__ ("nop"::)
 #define mb()  __asm__ __volatile__ (""   : : :"memory")
diff -urN linux-2.2.18-pre15/include/asm-m68k/uaccess.h linux/include/asm-m68k/uaccess.h
--- linux-2.2.18-pre15/include/asm-m68k/uaccess.h	Tue May 11 09:57:14 1999
+++ linux/include/asm-m68k/uaccess.h	Mon Dec  3 20:34:13 2001
@@ -801,18 +801,20 @@
  *
  * Return 0 for error
  */
-static inline long strlen_user(const char * src)
+#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
+static inline long strnlen_user(const char * src, long n)
 {
     long res = -(long) src;
     __asm__ __volatile__
-	("1: movesb (%1)+,%%d0\n"
+	("   subq #1,%2\n"
+	 "1: movesb (%1)+,%%d0\n"
 	 "12:tstb %%d0\n"
-	 "   jne 1b\n"
+	 "   dbeq %2,1b\n"
 	 "   addl %1,%0\n"
 	 "2:\n"
 	 ".section .fixup,\"ax\"\n"
 	 "   .even\n"
-	 "3: moveq %2,%0\n"
+	 "3: moveq #0,%0\n"
 	 "   jra 2b\n"
 	 ".previous\n"
 	 ".section __ex_table,\"a\"\n"
@@ -820,8 +822,8 @@
 	 "   .long 1b,3b\n"
 	 "   .long 12b,3b\n"
 	 ".previous"
-	 : "=d"(res), "=a"(src)
-	 : "i"(0), "0"(res), "1"(src)
+	 : "=d"(res), "=a"(src), "=d"(n)
+	 : "0"(res), "1"(src), "2"(n)
 	 : "d0");
     return res;
 }
diff -urN linux-2.2.18-pre15/include/asm-ppc/delay.h linux/include/asm-ppc/delay.h
--- linux-2.2.18-pre15/include/asm-ppc/delay.h	Tue Aug  4 16:06:36 1998
+++ linux/include/asm-ppc/delay.h	Mon Dec  3 20:34:13 2001
@@ -1,6 +1,8 @@
 #ifndef _PPC_DELAY_H
 #define _PPC_DELAY_H
 
+#include <asm/param.h>
+
 /*
  * Copyright 1996, Paul Mackerras.
  *
@@ -10,8 +12,6 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-extern unsigned long loops_per_sec;
-
 extern __inline__ void __delay(unsigned int loops)
 {
 	if (loops != 0)
@@ -23,10 +23,10 @@
 {
 	unsigned long loops;
 
-	/* compute (usecs * 2^32 / 10^6) * loops_per_sec / 2^32 */
+	/* compute (usecs * 2^32 / 10^6) * (loops_per_jiffy*HZ) / 2^32 */
 	usecs *= 0x10c6;		/* 2^32 / 10^6 */
 	__asm__("mulhwu %0,%1,%2" : "=r" (loops) :
-		"r" (usecs), "r" (loops_per_sec));
+		"r" (usecs), "r" (loops_per_jiffy*HZ));
 	__delay(loops);
 }
 
diff -urN linux-2.2.18-pre15/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h
--- linux-2.2.18-pre15/include/asm-ppc/smp.h	Tue Jan  4 10:12:24 2000
+++ linux/include/asm-ppc/smp.h	Mon Dec  3 20:34:13 2001
@@ -14,7 +14,7 @@
 #ifndef __ASSEMBLY__
 
 struct cpuinfo_PPC {
-	unsigned long loops_per_sec;
+	unsigned long loops_per_jiffy;
 	unsigned long pvr;
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
diff -urN linux-2.2.18-pre15/include/asm-sparc/delay.h linux/include/asm-sparc/delay.h
--- linux-2.2.18-pre15/include/asm-sparc/delay.h	Mon Jan 12 15:15:45 1998
+++ linux/include/asm-sparc/delay.h	Mon Dec  3 20:34:13 2001
@@ -1,4 +1,4 @@
-/* $Id: delay.h,v 1.10 1997/11/07 18:24:30 mj Exp $
+/* $Id: delay.h,v 1.10.6.1 2000/10/05 04:23:32 anton Exp $
  * delay.h: Linux delay routines on the Sparc.
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu).
@@ -6,8 +6,6 @@
 
 #ifndef __SPARC_DELAY_H
 #define __SPARC_DELAY_H
-
-extern unsigned long loops_per_sec;
 
 extern __inline__ void __delay(unsigned long loops)
 {
diff -urN linux-2.2.18-pre15/include/asm-sparc64/delay.h linux/include/asm-sparc64/delay.h
--- linux-2.2.18-pre15/include/asm-sparc64/delay.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc64/delay.h	Mon Dec  3 20:34:13 2001
@@ -1,4 +1,4 @@
-/* $Id: delay.h,v 1.7 1997/11/07 18:24:31 mj Exp $
+/* $Id: delay.h,v 1.7.6.1 2000/10/02 02:05:52 anton Exp $
  * delay.h: Linux delay routines on the V9.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu).
@@ -7,6 +7,8 @@
 #ifndef __SPARC64_DELAY_H
 #define __SPARC64_DELAY_H
 
+#include <asm/param.h>
+
 #ifdef __SMP__
 #include <asm/smp.h>
 #endif 
@@ -35,13 +37,13 @@
 "	: "=r" (usecs)
 	: "r" (usecs), "r" (lps));
 
-	__delay(usecs);
+	__delay(HZ * usecs);
 }
 
 #ifdef __SMP__
 #define __udelay_val cpu_data[smp_processor_id()].udelay_val
 #else
-#define __udelay_val loops_per_sec
+#define __udelay_val loops_per_jiffy
 #endif
 
 #define udelay(usecs) __udelay((usecs),__udelay_val)
diff -urN linux-2.2.18-pre15/include/linux/16c552.h linux/include/linux/16c552.h
--- linux-2.2.18-pre15/include/linux/16c552.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/16c552.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,181 @@
+/*
+ * Definitions for the 16c552 DACE
+ * (dual-asynchronous-communications-element) used on the GVP
+ * IO-Extender. 
+ *
+ * Basically this is two 16c550 uarts's and a parallel port, which is
+ * why the serial definitions should be valid for the 16c550 uart
+ * aswell.
+ *
+ * Data was taken from National Semiconductors duart 16c552
+ * data-sheets and the Texas Instruments DACE 16c552 data-sheets (the
+ * NS version of the chip is _non_ standard and their data-sheets did
+ * cost me several wasted hours of work).
+ *
+ * This file is (C) 1995 Jes Sorensen (jds@kom.auc.dk)
+ *
+ * Moved from drivers/char/ to include/linux/, because it's useful
+ * on more than just the one card. I'm using it on the hp300 DCA
+ * serial driver, for example.  
+ *      -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 05/1998
+ */
+
+#ifndef _16C552_H_
+#define _16C552_H_
+
+/* Serial stuff */
+
+struct uart_16c550 {
+	volatile u_char skip0;
+	volatile u_char RBR;
+	volatile u_char skip1;
+	volatile u_char IER;
+	volatile u_char skip2;
+	volatile u_char IIR;
+	volatile u_char skip3;
+	volatile u_char LCR;
+	volatile u_char skip4;
+	volatile u_char MCR;
+	volatile u_char skip5;
+	volatile u_char LSR;
+	volatile u_char skip6;
+	volatile u_char MSR;
+	volatile u_char skip7;
+	volatile u_char SCR;
+};
+
+#define THR RBR
+#define FCR IIR
+#define DLL RBR
+#define DLM IER
+#define AFR IIR
+
+/*
+ * Bit-defines for the various registers.
+ */
+
+
+/* IER */
+
+#define ERDAI         (1<<0)
+#define ETHREI        (1<<1)
+#define ELSI          (1<<2)
+#define EMSI          (1<<3)
+
+/* IIR - Interrupt Ident. Register */
+
+#define IRQ_PEND      (1<<0) /* NOTE: IRQ_PEND=0 implies irq pending */
+#define IRQ_ID1       (1<<1)
+#define IRQ_ID2       (1<<2)
+#define IRQ_ID3       (1<<3)
+#define FIFO_ENA0     (1<<6) /* Both these are set when FCR(1<<0)=1 */
+#define FIFO_ENA1     (1<<7)
+
+#define IRQ_RLS  (IRQ_ID1 | IRQ_ID2)
+#define IRQ_RDA  (IRQ_ID2)
+#define IRQ_CTI  (IRQ_ID2 | IRQ_ID3)
+#define IRQ_THRE (IRQ_ID1)
+#define IRQ_MS   0
+
+/* FCR - FIFO Control Register */
+
+#define FIFO_ENA      (1<<0)
+#define RCVR_FIFO_RES (1<<1)
+#define XMIT_FIFO_RES (1<<2)
+#define DMA_MODE_SEL  (1<<3)
+#define RCVR_TRIG_LSB (1<<6)
+#define RCVR_TRIG_MSB (1<<7)
+
+#define FIFO_TRIG_1   0x00
+#define FIFO_TRIG_4   RCVR_TRIG_LSB
+#define FIFO_TRIG_8   RCVR_TRIG_MSB
+#define FIFO_TRIG_14  RCVR_TRIG_LSB|RCVR_TRIG_MSB
+
+/* LCR - Line Control Register */
+
+#define WLS0          (1<<0)
+#define WLS1          (1<<1)
+#define STB           (1<<2)
+#define PEN           (1<<3)
+#define EPS           (1<<4)
+#define STICK_PARITY  (1<<5)
+#define SET_BREAK     (1<<6)
+#define DLAB          (1<<7)
+
+#define data_5bit      0x00
+#define data_6bit      0x01
+#define data_7bit      0x02
+#define data_8bit      0x03
+
+
+/* MCR - Modem Control Register */
+
+#define DTR           (1<<0)
+#define RTS           (1<<1)
+#define OUT1          (1<<2)
+#define OUT2          (1<<3)
+#define LOOP          (1<<4)
+
+/* LSR - Line Status Register */
+
+#define DR            (1<<0)
+#define OE            (1<<1)
+#define PE            (1<<2)
+#define FE            (1<<3)
+#define BI            (1<<4)
+#define THRE          (1<<5)
+#define TEMT          (1<<6)
+#define RCVR_FIFO_ERR (1<<7)
+
+/* MSR - Modem Status Register */
+
+#define DCTS          (1<<0)
+#define DDSR          (1<<1)
+#define TERI          (1<<2)
+#define DDCD          (1<<3)
+#define CTS           (1<<4)
+#define DSR           (1<<5)
+#define RING_I        (1<<6)
+#define DCD           (1<<7)
+
+/* AFR - Alternate Function Register */
+
+#define CONCUR_WRITE  (1<<0)
+#define BAUDOUT       (1<<1)
+#define RXRDY         (1<<2)
+
+/* Parallel stuff */
+
+/*
+ * Unfortunately National Semiconductors did not supply the
+ * specifications for the parallel port in the chip :-(
+ * TI succed though, so here they are :-)
+ */
+struct IOEXT_par {
+	volatile u_char skip0;
+	volatile u_char DATA;
+	volatile u_char skip1;
+	volatile u_char STATUS;
+	volatile u_char skip2;
+	volatile u_char CTRL;
+};
+
+/* 
+ * bit defines for 16c552 (8255) parallel status port
+ */
+#define LP_PBUSY	0x80  /* inverted input, active high */
+#define LP_PACK		0x40  /* unchanged input, active low */
+#define LP_POUTPA	0x20  /* unchanged input, active high */
+#define LP_PSELECD	0x10  /* unchanged input, active high */
+#define LP_PERRORP	0x08  /* unchanged input, active low */
+
+/* 
+ * defines for 16c552 (8255) parallel control port
+ */
+#define LP_PINTEN	0x10  /* high to read data in or-ed with data out */
+#define LP_PSELECP	0x08  /* inverted output, active low */
+#define LP_PINITP	0x04  /* unchanged output, active low */
+#define LP_PAUTOLF	0x02  /* inverted output, active low */
+#define LP_PSTROBE	0x01  /* short high output on raising edge */
+
+#endif
diff -urN linux-2.2.18-pre15/include/linux/dcache.h linux/include/linux/dcache.h
--- linux-2.2.18-pre15/include/linux/dcache.h	Mon Dec  3 20:33:37 2001
+++ linux/include/linux/dcache.h	Mon Dec  3 20:34:13 2001
@@ -100,6 +100,12 @@
 					 * renamed" and has to be
 					 * deleted on the last dput()
 					 */
+#define	DCACHE_NFSD_DISCONNECTED 0x0004	/* This dentry is not currently connected to the
+					 * dcache tree. Its parent will either be itself,
+					 * or will have this flag as well.
+					 * If this dentry points to a directory, then
+					 * s_nfsd_free_path semaphore will be down
+					 */
 
 /*
  * d_drop() unhashes the entry from the parent
diff -urN linux-2.2.18-pre15/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- linux-2.2.18-pre15/include/linux/ext2_fs.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/ext2_fs.h	Mon Dec  3 20:34:13 2001
@@ -238,7 +238,7 @@
 		} masix1;
 	} osd1;				/* OS dependent 1 */
 	__u32	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
-	__u32	i_version;	/* File version (for NFS) */
+	__u32	i_generation;	/* File version (for NFS) */
 	__u32	i_file_acl;	/* File ACL */
 	__u32	i_dir_acl;	/* Directory ACL */
 	__u32	i_faddr;	/* Fragment address */
diff -urN linux-2.2.18-pre15/include/linux/ext2_fs_i.h linux/include/linux/ext2_fs_i.h
--- linux-2.2.18-pre15/include/linux/ext2_fs_i.h	Thu Apr  2 13:39:51 1998
+++ linux/include/linux/ext2_fs_i.h	Mon Dec  3 20:34:13 2001
@@ -29,7 +29,7 @@
 	__u32	i_file_acl;
 	__u32	i_dir_acl;
 	__u32	i_dtime;
-	__u32	i_version;
+	__u32	not_used_1;	/* FIX: not used/ 2.2 placeholder */
 	__u32	i_block_group;
 	__u32	i_next_alloc_block;
 	__u32	i_next_alloc_goal;
diff -urN linux-2.2.18-pre15/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.2.18-pre15/include/linux/fs.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/fs.h	Mon Dec  3 20:34:13 2001
@@ -571,6 +571,15 @@
 	 * even looking at it. You had been warned.
 	 */
 	struct semaphore s_vfs_rename_sem;	/* Kludge */
+
+	/* The next field is used by knfsd when converting a (inode number based)
+	 * file handle into a dentry. As it builds a path in the dcache tree from
+	 * the bottom up, there may for a time be a subpath of dentrys which is not
+	 * connected to the main tree.  This semaphore ensure that there is only ever
+	 * one such free path per filesystem.  Note that unconnected files (or other
+	 * non-directories) are allowed, but not unconnected diretories.
+	 */
+	struct semaphore s_nfsd_free_path_sem;
 };
 
 /*
diff -urN linux-2.2.18-pre15/include/linux/hdreg.h linux/include/linux/hdreg.h
--- linux-2.2.18-pre15/include/linux/hdreg.h	Wed May  3 17:16:51 2000
+++ linux/include/linux/hdreg.h	Mon Dec  3 20:34:13 2001
@@ -246,7 +246,7 @@
 	unsigned short	word123;	/* reserved (word 123) */
 	unsigned short	word124;	/* reserved (word 124) */
 	unsigned short	word125;	/* reserved (word 125) */
-	unsigned short	word126;	/* reserved (word 126) */
+	unsigned short	last_lun;	/* last lun on device */
 	unsigned short	word127;	/* reserved (word 127) */
 	unsigned short	security;	/* bits 0:support 1:enabled 2:locked 3:frozen */
 	unsigned short	reserved[127];
diff -urN linux-2.2.18-pre15/include/linux/kcomp.h linux/include/linux/kcomp.h
--- linux-2.2.18-pre15/include/linux/kcomp.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/kcomp.h	Mon Dec  3 20:34:13 2001
@@ -33,6 +33,24 @@
 #define netif_queue_stopped(dev)	dev->tbusy
 #define netif_running(dev)		dev->start
 
+/* hot-(un)plugging stuff */
+static inline int netif_device_present(struct net_device *dev)
+{
+	return	test_bit(0, &dev->start);
+}
+
+static inline void netif_device_detach(struct net_device *dev)
+{
+	if ( test_and_clear_bit(0, &dev->start) )
+		netif_stop_queue(dev);
+}
+
+static inline void netif_device_attach(struct net_device *dev)
+{
+	if ( !test_and_set_bit(0, &dev->start) )
+		netif_wake_queue(dev);
+}
+
 #define NET_XMIT_SUCCESS	0
 #define NET_XMIT_DROP		1
 #define NET_XMIT_CN		2
diff -urN linux-2.2.18-pre15/include/linux/kmod.h linux/include/linux/kmod.h
--- linux-2.2.18-pre15/include/linux/kmod.h	Mon Sep  4 10:39:28 2000
+++ linux/include/linux/kmod.h	Mon Dec  3 20:34:13 2001
@@ -7,6 +7,9 @@
 #ifdef CONFIG_KMOD
 extern int request_module(const char * name);
 extern int exec_usermodehelper(char *program_path, char *argv[], char *envp[]);
+#ifdef CONFIG_HOTPLUG
+extern char hotplug_path[];
+#endif
 #else
 #include <linux/errno.h>
 
diff -urN linux-2.2.18-pre15/include/linux/lockd/debug.h linux/include/linux/lockd/debug.h
--- linux-2.2.18-pre15/include/linux/lockd/debug.h	Tue May 11 10:36:15 1999
+++ linux/include/linux/lockd/debug.h	Mon Dec  3 20:34:13 2001
@@ -45,6 +45,7 @@
 #define NLMDBG_CLNTSUBS		0x0020
 #define NLMDBG_SVCSUBS		0x0040
 #define NLMDBG_HOSTCACHE	0x0080
+#define NLMDBG_XDR		0x0100
 #define NLMDBG_ALL		0x7fff
 
 
diff -urN linux-2.2.18-pre15/include/linux/lockd/lockd.h linux/include/linux/lockd/lockd.h
--- linux-2.2.18-pre15/include/linux/lockd/lockd.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/lockd/lockd.h	Mon Dec  3 20:34:13 2001
@@ -17,6 +17,7 @@
 #include <linux/nfsd/nfsfh.h>
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/debug.h>
 
 /*
@@ -112,6 +113,7 @@
  */
 extern struct rpc_program	nlm_program;
 extern struct svc_procedure	nlmsvc_procedures[];
+extern struct svc_procedure     nlmsvc_procedures4[];
 extern unsigned long		nlmsvc_grace_period;
 extern unsigned long		nlmsvc_timeout;
 
diff -urN linux-2.2.18-pre15/include/linux/lockd/xdr4.h linux/include/linux/lockd/xdr4.h
--- linux-2.2.18-pre15/include/linux/lockd/xdr4.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/xdr4.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,43 @@
+/*
+ * linux/include/linux/lockd/xdr.h
+ *
+ * XDR types for the NLM protocol
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LOCKD_XDR4_H
+#define LOCKD_XDR4_H
+
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/lockd/xdr.h>
+
+extern u32	nlm4_granted, nlm4_lck_denied, nlm4_lck_denied_nolocks,
+		nlm4_lck_blocked, nlm4_lck_denied_grace_period, nlm4_deadlock,
+		nlm4_rofs, nlm4_stale_fh, nlm4_fbig, nlm4_failed;
+
+#define NLMSVC_XDRSIZE		sizeof(struct nlm_args)
+
+int	nlm4svc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_cancargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_unlockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_encode_void(struct svc_rqst *, u32 *, void *);
+int	nlm4svc_decode_void(struct svc_rqst *, u32 *, void *);
+int	nlm4svc_decode_shareargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_shareres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_notify(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_reboot(struct svc_rqst *, u32 *, struct nlm_reboot *);
+/*
+int	nlmclt_encode_testargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_lockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_cancargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_unlockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+ */
+
+#endif /* LOCKD_XDR4_H */
diff -urN linux-2.2.18-pre15/include/linux/m68kserial.h linux/include/linux/m68kserial.h
--- linux-2.2.18-pre15/include/linux/m68kserial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/m68kserial.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,16 @@
+/*
+ * include/linux/m68kserial.h
+ *
+ * Copyright (C) 1992 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_M68KSERIAL_H
+#define _LINUX_M68KSERIAL_H
+
+
+#include <asm/m68kserial.h>
+
+#endif /* _LINUX_M68KSERIAL_H*/
diff -urN linux-2.2.18-pre15/include/linux/mc6821.h linux/include/linux/mc6821.h
--- linux-2.2.18-pre15/include/linux/mc6821.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/mc6821.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,51 @@
+#ifndef _MC6821_H_
+#define _MC6821_H_
+
+/*
+ * This file describes the memery mapping of the MC6821 PIA.
+ * The unions describe overlayed registers. Which of them is used is
+ * determined by bit 2 of the corresponding control register.
+ * this files expects the PIA_REG_PADWIDTH to be defined the numeric
+ * value of the register spacing.
+ *
+ * Data came from MFC-31-Developer Kit (from Ralph Seidel,
+ * zodiac@darkness.gun.de) and Motorola Data Sheet (from 
+ * Richard Hirst, srh@gpt.co.uk)
+ *
+ * 6.11.95 copyright Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ */
+
+#ifndef PIA_REG_PADWIDTH
+#define PIA_REG_PADWIDTH 255
+#endif
+
+struct pia {
+	union {
+		volatile u_char pra;
+		volatile u_char ddra;
+	} ua;
+	u_char pad1[PIA_REG_PADWIDTH];
+	volatile u_char cra;
+	u_char pad2[PIA_REG_PADWIDTH];
+	union {
+		volatile u_char prb;
+		volatile u_char ddrb;
+	} ub;
+	u_char pad3[PIA_REG_PADWIDTH];
+	volatile u_char crb;
+	u_char pad4[PIA_REG_PADWIDTH];
+};
+
+#define ppra ua.pra
+#define pddra ua.ddra
+#define pprb ub.prb
+#define pddrb ub.ddrb
+
+#define PIA_C1_ENABLE_IRQ (1<<0)
+#define PIA_C1_LOW_TO_HIGH (1<<1)
+#define PIA_DDR (1<<2)
+#define PIA_IRQ2 (1<<6)
+#define PIA_IRQ1 (1<<7)
+
+#endif
diff -urN linux-2.2.18-pre15/include/linux/nfsd/cache.h linux/include/linux/nfsd/cache.h
--- linux-2.2.18-pre15/include/linux/nfsd/cache.h	Mon Dec 28 14:09:59 1998
+++ linux/include/linux/nfsd/cache.h	Mon Dec  3 20:34:13 2001
@@ -25,9 +25,11 @@
 	unsigned char		c_state,	/* unused, inprog, done */
 				c_type,		/* status, buffer */
 				c_secure : 1;	/* req came from port < 1024 */
-	struct in_addr		c_client;
+	struct sockaddr_in	c_addr;
 	u32			c_xid;
+	u32			c_prot;
 	u32			c_proc;
+	u32			c_vers;
 	unsigned long		c_timestamp;
 	union {
 		struct svc_buf	u_buffer;
diff -urN linux-2.2.18-pre15/include/linux/nfsd/const.h linux/include/linux/nfsd/const.h
--- linux-2.2.18-pre15/include/linux/nfsd/const.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/nfsd/const.h	Mon Dec  3 20:34:13 2001
@@ -1,20 +1,17 @@
 /*
- * include/linux/nfsd/nfsconst.h
+ * include/linux/nfsd/const.h
  *
  * Various constants related to NFS.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef __NFSCONST_H__
-#define __NFSCONST_H__
+#ifndef _LINUX_NFSD_CONST_H
+#define _LINUX_NFSD_CONST_H
 
-#include <linux/limits.h>
-#include <linux/types.h>
-#include <linux/unistd.h>
-#include <linux/dirent.h>
-#include <linux/fs.h>
 #include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 
 #define NFS_FHSIZE		32
 #define NFS_MAXPATHLEN		1024
@@ -30,12 +27,8 @@
  */
 #define NFSSVC_MAXBLKSIZE	8192
 
-#define NFS2_MAXPATHLEN		1024
-#define NFS2_MAXNAMLEN		255
 #define NFS2_COOKIESIZE		4
 
-#define NFS3_MAXPATHLEN		PATH_MAX
-#define NFS3_MAXNAMLEN		NAME_MAX
 #define NFS3_COOKIEVERFSIZE	8
 #define NFS3_CREATEVERFSIZE	8
 #define NFS3_WRITEVERFSIZE	8
@@ -46,43 +39,6 @@
 # define NFS_SUPER_MAGIC	0x6969
 #endif
 
-/*
- * NFS stats. The good thing with these values is that NFSv3 errors are
- * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
- * no-one uses anyway), so we can happily mix code as long as we make sure
- * no NFSv3 errors are returned to NFSv2 clients.
- */
-#define NFS_OK			0		/* v2 v3 */
-#define NFSERR_PERM		1		/* v2 v3 */
-#define NFSERR_NOENT		2		/* v2 v3 */
-#define NFSERR_IO		5		/* v2 v3 */
-#define NFSERR_NXIO		6		/* v2 v3 */
-#define NFSERR_ACCES		13		/* v2 v3 */
-#define NFSERR_EXIST		17		/* v2 v3 */
-#define NFSERR_XDEV		18		/*    v3 */
-#define NFSERR_NODEV		19		/* v2 v3 */
-#define NFSERR_NOTDIR		20		/* v2 v3 */
-#define NFSERR_ISDIR		21		/* v2 v3 */
-#define NFSERR_INVAL		22		/*    v3 */
-#define NFSERR_FBIG		27		/* v2 v3 */
-#define NFSERR_NOSPC		28		/* v2 v3 */
-#define NFSERR_ROFS		30		/* v2 v3 */
-#define NFSERR_MLINK		31		/*    v3 */
-#define NFSERR_NAMETOOLONG	63		/* v2 v3 */
-#define NFSERR_NOTEMPTY		66		/* v2 v3 */
-#define NFSERR_DQUOT		69		/* v2 v3 */
-#define NFSERR_STALE		70		/* v2 v3 */
-#define NFSERR_REMOTE		71		/*    v3 */
-#define NFSERR_WFLUSH		99		/* v2    */
-#define NFSERR_BADHANDLE	10001		/*    v3 */
-#define NFSERR_NOT_SYNC		10002		/*    v3 */
-#define NFSERR_BAD_COOKIE	10003		/*    v3 */
-#define NFSERR_NOTSUPP		10004		/*    v3 */
-#define NFSERR_TOOSMALL		10005		/*    v3 */
-#define NFSERR_SERVERFAULT	10006		/*    v3 */
-#define NFSERR_BADTYPE		10007		/*    v3 */
-#define NFSERR_JUKEBOX		10008		/*    v3 */
-
 #endif /* __KERNEL__ */
 
-#endif /* __NFSCONST_H__ */
+#endif /* _LINUX_NFSD_CONST_H */
diff -urN linux-2.2.18-pre15/include/linux/nfsd/export.h linux/include/linux/nfsd/export.h
--- linux-2.2.18-pre15/include/linux/nfsd/export.h	Tue May 11 10:36:17 1999
+++ linux/include/linux/nfsd/export.h	Mon Dec  3 20:34:13 2001
@@ -4,16 +4,17 @@
  * Public declarations for NFS exports. The definitions for the
  * syscall interface are in nfsctl.h
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef NFSD_EXPORT_H
 #define NFSD_EXPORT_H
 
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/fs.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/types.h>
+# include <linux/in.h>
+#endif
 
 /*
  * Important limits for the exports stuff.
@@ -34,8 +35,10 @@
 #define NFSEXP_UIDMAP		0x0040
 #define NFSEXP_KERBEROS		0x0080		/* not available */
 #define NFSEXP_SUNSECURE	0x0100
-#define NFSEXP_CROSSMNT		0x0200		/* not available */
-#define NFSEXP_ALLFLAGS		0x03FF
+#define NFSEXP_CROSSMNT		0x0200
+#define NFSEXP_NOSUBTREECHECK	0x0400
+#define	NFSEXP_NOAUTHNLM	0x0800		/* Don't authenticate NLM requests - just trust */
+#define NFSEXP_ALLFLAGS		0x0FFF
 
 
 #ifdef __KERNEL__
diff -urN linux-2.2.18-pre15/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- linux-2.2.18-pre15/include/linux/nfsd/nfsd.h	Tue May 11 10:36:17 1999
+++ linux/include/linux/nfsd/nfsd.h	Mon Dec  3 20:34:13 2001
@@ -4,7 +4,7 @@
  * Hodge-podge collection of knfsd-related stuff.
  * I will sort this out later.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef LINUX_NFSD_NFSD_H
@@ -24,18 +24,21 @@
 /*
  * nfsd version
  */
-#define NFSD_VERSION		"0.4"
+#define NFSD_VERSION		"0.5"
 
 #ifdef __KERNEL__
 /*
  * Special flags for nfsd_permission. These must be different from MAY_READ,
  * MAY_WRITE, and MAY_EXEC.
  */
-#define MAY_NOP			0
-#define MAY_SATTR		8
-#define MAY_TRUNC		16
-#if (MAY_SATTR | MAY_TRUNC) & (MAY_READ | MAY_WRITE | MAY_EXEC)
-# error "please use a different value for MAY_SATTR or MAY_TRUNC."
+#define MAY_NOP			0x00000000
+#define MAY_SATTR		0x00000008
+#define MAY_TRUNC		0x00000010
+#define MAY_LOCK		0x00000020
+#define NO_OWNER_OVERRIDE	0x00000040
+
+#if (MAY_SATTR | MAY_TRUNC | MAY_LOCK | NO_OWNER_OVERRIDE) & (MAY_READ | MAY_WRITE | MAY_EXEC)
+# error "please use a different value for MAY_SATTR or MAY_TRUNC or MAY_LOCK or NO_OWNER_OVERRIDE."
 #endif
 #define MAY_CREATE		(MAY_EXEC|MAY_WRITE)
 #define MAY_REMOVE		(MAY_EXEC|MAY_WRITE|MAY_TRUNC)
@@ -61,6 +64,9 @@
  * Procedure table for NFSv2
  */
 extern struct svc_procedure	nfsd_procedures2[];
+#ifdef CONFIG_NFSD_V3
+extern struct svc_procedure	nfsd_procedures3[];
+#endif /* CONFIG_NFSD_V3 */
 extern struct svc_program	nfsd_program;
 
 /*
@@ -74,11 +80,20 @@
 void		nfsd_racache_shutdown(void);
 int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
 				const char *, int, struct svc_fh *);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_setattr(struct svc_rqst *, struct svc_fh *,
 				struct iattr *);
 int		nfsd_create(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, struct iattr *attrs,
 				int type, dev_t rdev, struct svc_fh *res);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_create_v3(struct svc_rqst *, struct svc_fh *,
+				char *name, int len, struct iattr *attrs,
+				struct svc_fh *res, int createmode,
+				u32 *verifier);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_open(struct svc_rqst *, struct svc_fh *, int,
 				int, struct file *);
 void		nfsd_close(struct file *);
@@ -90,7 +105,7 @@
 				char *, int *);
 int		nfsd_symlink(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, char *path, int plen,
-				struct svc_fh *res);
+				struct svc_fh *res, struct iattr *);
 int		nfsd_link(struct svc_rqst *, struct svc_fh *,
 				char *, int, struct svc_fh *);
 int		nfsd_rename(struct svc_rqst *,
@@ -104,9 +119,13 @@
 				unsigned long size);
 int		nfsd_readdir(struct svc_rqst *, struct svc_fh *,
 				loff_t, encode_dent_fn,
-				u32 *buffer, int *countp);
+				u32 *buffer, int *countp, u32 *verf);
 int		nfsd_statfs(struct svc_rqst *, struct svc_fh *,
 				struct statfs *);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_commit(struct svc_rqst *, struct svc_fh *,
+				off_t, unsigned long);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_notify_change(struct inode *, struct iattr *);
 int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
@@ -146,6 +165,7 @@
 		nfserr_rofs,
 		nfserr_mlink,
 		nfserr_nametoolong,
+		nfserr_notempty,
 		nfserr_dquot,
 		nfserr_stale,
 		nfserr_remote,
diff -urN linux-2.2.18-pre15/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- linux-2.2.18-pre15/include/linux/nfsd/nfsfh.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/nfsd/nfsfh.h	Mon Dec  3 20:34:13 2001
@@ -11,12 +11,15 @@
  * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef NFSD_FH_H
-#define NFSD_FH_H
+#ifndef _LINUX_NFSD_FH_H
+#define _LINUX_NFSD_FH_H
 
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/fs.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/types.h>
+# include <linux/string.h>
+# include <linux/fs.h>
+#endif
 #include <linux/nfsd/const.h>
 #include <linux/nfsd/debug.h>
 
@@ -83,12 +86,32 @@
 	struct knfs_fh		fh_handle;	/* FH data */
 	struct dentry *		fh_dentry;	/* validated dentry */
 	struct svc_export *	fh_export;	/* export pointer */
-	size_t			fh_pre_size;	/* size before operation */
+#ifdef CONFIG_NFSD_V3
+	unsigned char		fh_post_saved;	/* post-op attrs saved */
+	unsigned char		fh_pre_saved;	/* pre-op attrs saved */
+#endif /* CONFIG_NFSD_V3 */
+	unsigned char		fh_locked;	/* inode locked by us */
+
+#ifdef CONFIG_NFSD_V3
+	/* Pre-op attributes saved during fh_lock */
+	__u64			fh_pre_size;	/* size before operation */
 	time_t			fh_pre_mtime;	/* mtime before oper */
 	time_t			fh_pre_ctime;	/* ctime before oper */
-	unsigned long		fh_post_version;/* inode version after oper */
-	unsigned char		fh_locked;	/* inode locked by us */
-	unsigned char		fh_dverified;	/* dentry has been checked */
+
+	/* Post-op attributes saved in fh_unlock */
+	umode_t			fh_post_mode;	/* i_mode */
+	nlink_t			fh_post_nlink;	/* i_nlink */
+	uid_t			fh_post_uid;	/* i_uid */
+	gid_t			fh_post_gid;	/* i_gid */
+	__u64			fh_post_size;	/* i_size */
+	unsigned long		fh_post_blocks; /* i_blocks */
+	unsigned long		fh_post_blksize;/* i_blksize */
+	kdev_t			fh_post_rdev;	/* i_rdev */
+	time_t			fh_post_atime;	/* i_atime */
+	time_t			fh_post_mtime;	/* i_mtime */
+	time_t			fh_post_ctime;	/* i_ctime */
+#endif /* CONFIG_NFSD_V3 */
+
 } svc_fh;
 
 /*
@@ -105,18 +128,11 @@
 void	fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
 void	fh_update(struct svc_fh *);
 void	fh_put(struct svc_fh *);
-void	nfsd_fh_flush(kdev_t);
-void	nfsd_fh_init(void);
-void	nfsd_fh_shutdown(void);
-void	nfsd_fh_free(void);
-
-void	expire_all(void);
-void	expire_by_dentry(struct dentry *);
 
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
 {
-	if (src->fh_dverified || src->fh_locked) {
+	if (src->fh_dentry || src->fh_locked) {
 		struct dentry *dentry = src->fh_dentry;
 		printk(KERN_ERR "fh_copy: copying %s/%s, already verified!\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -133,6 +149,53 @@
 	return fhp;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * Fill in the pre_op attr for the wcc data
+ */
+static inline void
+fill_pre_wcc(struct svc_fh *fhp)
+{
+        struct inode    *inode;
+
+        inode = fhp->fh_dentry->d_inode;
+        if (!fhp->fh_pre_saved) {
+                fhp->fh_pre_mtime = inode->i_mtime;
+                        fhp->fh_pre_ctime = inode->i_ctime;
+                        fhp->fh_pre_size  = inode->i_size;
+                        fhp->fh_pre_saved = 1;
+        }
+        fhp->fh_locked = 1;
+}
+
+/*
+ * Fill in the post_op attr for the wcc data
+ */
+static inline void
+fill_post_wcc(struct svc_fh *fhp)
+{
+        struct inode    *inode = fhp->fh_dentry->d_inode;
+
+        if (fhp->fh_post_saved)
+                printk("nfsd: inode locked twice during operation.\n");
+
+        fhp->fh_post_mode       = inode->i_mode;
+        fhp->fh_post_nlink      = inode->i_nlink;
+        fhp->fh_post_uid        = inode->i_uid;
+        fhp->fh_post_gid        = inode->i_gid;
+        fhp->fh_post_size       = inode->i_size;
+        fhp->fh_post_blksize    = inode->i_blksize;
+        fhp->fh_post_blocks     = inode->i_blocks;
+        fhp->fh_post_rdev       = inode->i_rdev;
+        fhp->fh_post_atime      = inode->i_atime;
+        fhp->fh_post_mtime      = inode->i_mtime;
+        fhp->fh_post_ctime      = inode->i_ctime;
+        fhp->fh_post_saved      = 1;
+        fhp->fh_locked          = 0;
+}
+#endif /* CONFIG_NFSD_V3 */
+
+
 /*
  * Lock a file handle/inode
  */
@@ -142,11 +205,10 @@
 	struct dentry	*dentry = fhp->fh_dentry;
 	struct inode	*inode;
 
-	/*
 	dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
-			SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked);
-	 */
-	if (!fhp->fh_dverified) {
+			SVCFH_DEV(fhp), (long)SVCFH_INO(fhp), fhp->fh_locked);
+
+	if (!fhp->fh_dentry) {
 		printk(KERN_ERR "fh_lock: fh not verified!\n");
 		return;
 	}
@@ -158,9 +220,11 @@
 
 	inode = dentry->d_inode;
 	down(&inode->i_sem);
-	if (!fhp->fh_pre_mtime)
-		fhp->fh_pre_mtime = inode->i_mtime;
+#ifdef CONFIG_NFSD_V3
+	fill_pre_wcc(fhp);
+#else
 	fhp->fh_locked = 1;
+#endif /* CONFIG_NFSD_V3 */
 }
 
 /*
@@ -169,25 +233,23 @@
 static inline void
 fh_unlock(struct svc_fh *fhp)
 {
-	if (!fhp->fh_dverified)
+	if (!fhp->fh_dentry)
 		printk(KERN_ERR "fh_unlock: fh not verified!\n");
 
 	if (fhp->fh_locked) {
+#ifdef CONFIG_NFSD_V3
+		fill_post_wcc(fhp);
+		up(&fhp->fh_dentry->d_inode->i_sem);
+#else
 		struct dentry *dentry = fhp->fh_dentry;
 		struct inode *inode = dentry->d_inode;
 
-		if (!fhp->fh_post_version)
-			fhp->fh_post_version = inode->i_version;
 		fhp->fh_locked = 0;
 		up(&inode->i_sem);
+#endif /* CONFIG_NFSD_V3 */
 	}
 }
-
-/*
- * This is a long term cache to help find renamed files.
- */
-void add_to_rename_cache(ino_t new_dirino, kdev_t dev, ino_t dirino, ino_t ino);
-
 #endif /* __KERNEL__ */
 
-#endif /* NFSD_FH_H */
+
+#endif /* _LINUX_NFSD_FH_H */
diff -urN linux-2.2.18-pre15/include/linux/nfsd/stats.h linux/include/linux/nfsd/stats.h
--- linux-2.2.18-pre15/include/linux/nfsd/stats.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/nfsd/stats.h	Mon Dec  3 20:34:13 2001
@@ -13,12 +13,11 @@
 	unsigned int	rchits;		/* repcache hits */
 	unsigned int	rcmisses;	/* repcache hits */
 	unsigned int	rcnocache;	/* uncached reqs */
-	unsigned int	fh_cached;	/* dentry cached */
-	unsigned int	fh_valid;	/* dentry validated */
-	unsigned int	fh_fixup;	/* dentry fixup validated */
 	unsigned int	fh_lookup;	/* new lookup required */
+	unsigned int	fh_anon;	
+	unsigned int	fh_nocache_nondir;	
+	unsigned int	fh_nocache_dir;	
 	unsigned int	fh_stale;	/* FH stale error */
-	unsigned int    fh_concurrent;	/* concurrent request */
 };
 
 #ifdef __KERNEL__
diff -urN linux-2.2.18-pre15/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- linux-2.2.18-pre15/include/linux/nfsd/syscall.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/nfsd/syscall.h	Mon Dec  3 20:34:13 2001
@@ -3,15 +3,18 @@
  *
  * This file holds all declarations for the knfsd syscall interface.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef NFSD_SYSCALL_H
 #define NFSD_SYSCALL_H
 
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/socket.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/config.h>
+# include <linux/types.h>
+# include <linux/in.h>
+#endif 
 #include <linux/posix_types.h>
 #include <linux/nfsd/const.h>
 #include <linux/nfsd/export.h>
diff -urN linux-2.2.18-pre15/include/linux/nfsd/xdr3.h linux/include/linux/nfsd/xdr3.h
--- linux-2.2.18-pre15/include/linux/nfsd/xdr3.h	Mon Apr  7 11:35:32 1997
+++ linux/include/linux/nfsd/xdr3.h	Mon Dec  3 20:34:13 2001
@@ -3,17 +3,18 @@
  *
  * XDR types for NFSv3 in nfsd.
  *
- * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1996-1998, Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef LINUX_NFSD_XDR3_H
-#define LINUX_NFSD_XDR3_H
+#ifndef _LINUX_NFSD_XDR3_H
+#define _LINUX_NFSD_XDR3_H
 
 #include <linux/nfsd/xdr.h>
 
 struct nfsd3_sattrargs {
 	struct svc_fh		fh;
 	struct iattr		attrs;
+	int			check_guard;
 	time_t			guardtime;
 };
 
@@ -88,7 +89,7 @@
 
 struct nfsd3_readdirargs {
 	struct svc_fh		fh;
-	__u32			cookie;
+	__u64			cookie;
 	__u32			dircount;
 	__u32			count;
 	__u32 *			verf;
@@ -97,7 +98,7 @@
 struct nfsd3_commitargs {
 	struct svc_fh		fh;
 	__u64			offset;
-	__u64			count;
+	__u32			count;
 };
 
 struct nfsd3_attrstat {
@@ -105,7 +106,8 @@
 	struct svc_fh		fh;
 };
 
-struct nfsd3_lookupres  {
+/* LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD */
+struct nfsd3_diropres  {
 	__u32			status;
 	struct svc_fh		dirfh;
 	struct svc_fh		fh;
@@ -137,12 +139,6 @@
 	int			committed;
 };
 
-struct nfsd3_createres {
-	__u32			status;
-	struct svc_fh		dirfh;
-	struct svc_fh		fh;
-};
-
 struct nfsd3_renameres {
 	__u32			status;
 	struct svc_fh		ffh;
@@ -158,10 +154,11 @@
 struct nfsd3_readdirres {
 	__u32			status;
 	struct svc_fh		fh;
-	__u32 *			list_end;
+	int			count;
+	__u32			verf[2];
 };
 
-struct nfsd3_statfsres {
+struct nfsd3_fsstatres {
 	__u32			status;
 	struct statfs		stats;
 	__u32			invarsec;
@@ -184,6 +181,8 @@
 	__u32			status;
 	__u32			p_link_max;
 	__u32			p_name_max;
+	__u32			p_no_trunc;
+	__u32			p_chown_restricted;
 	__u32			p_case_insensitive;
 	__u32			p_case_preserving;
 };
@@ -194,7 +193,7 @@
 };
 
 /* dummy type for release */
-struct nfsd3_fhandle2 {
+struct nfsd3_fhandle_pair {
 	__u32			dummy;
 	struct svc_fh		fh1;
 	struct svc_fh		fh2;
@@ -213,16 +212,15 @@
 	struct nfsd3_linkargs		linkargs;
 	struct nfsd3_symlinkargs	symlinkargs;
 	struct nfsd3_readdirargs	readdirargs;
-	struct nfsd3_lookupres 		lookupres;
+	struct nfsd3_diropres 		diropres;
 	struct nfsd3_accessres		accessres;
 	struct nfsd3_readlinkres	readlinkres;
 	struct nfsd3_readres		readres;
 	struct nfsd3_writeres		writeres;
-	struct nfsd3_createres		createres;
 	struct nfsd3_renameres		renameres;
 	struct nfsd3_linkres		linkres;
 	struct nfsd3_readdirres		readdirres;
-	struct nfsd3_statfsres		statfsres;
+	struct nfsd3_fsstatres		fsstatres;
 	struct nfsd3_fsinfores		fsinfores;
 	struct nfsd3_pathconfres	pathconfres;
 	struct nfsd3_commitres		commitres;
@@ -230,39 +228,87 @@
 
 #define NFS3_SVC_XDRSIZE		sizeof(union nfsd3_xdrstore)
 
-void nfsxdr_init(void);
-
 int nfs3svc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *);
-int nfs3svc_decode_sattr3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_sattrargs(struct svc_rqst *, u32 *,
 				struct nfsd3_sattrargs *);
-int nfs3svc_decode_dirop3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_diropargs(struct svc_rqst *, u32 *,
 				struct nfsd3_diropargs *);
-int nfs3svc_decode_read3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_accessargs(struct svc_rqst *, u32 *,
+				struct nfsd3_accessargs *);
+int nfs3svc_decode_readargs(struct svc_rqst *, u32 *,
 				struct nfsd3_readargs *);
-int nfs3svc_decode_write3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_writeargs(struct svc_rqst *, u32 *,
 				struct nfsd3_writeargs *);
-int nfs3svc_decode_create3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_createargs(struct svc_rqst *, u32 *,
+				struct nfsd3_createargs *);
+int nfs3svc_decode_mkdirargs(struct svc_rqst *, u32 *,
 				struct nfsd3_createargs *);
-int nfs3svc_decode_rename3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_mknodargs(struct svc_rqst *, u32 *,
+				struct nfsd3_mknodargs *);
+int nfs3svc_decode_renameargs(struct svc_rqst *, u32 *,
 				struct nfsd3_renameargs *);
-int nfs3svc_decode_link3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_linkargs(struct svc_rqst *, u32 *,
 				struct nfsd3_linkargs *);
-int nfs3svc_decode_symlink3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_symlinkargs(struct svc_rqst *, u32 *,
 				struct nfsd3_symlinkargs *);
-int nfs3svc_decode_readdir3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_readdirargs(struct svc_rqst *, u32 *,
 				struct nfsd3_readdirargs *);
+int nfs3svc_decode_readdirplusargs(struct svc_rqst *, u32 *,
+				struct nfsd3_readdirargs *);
+int nfs3svc_decode_commitargs(struct svc_rqst *, u32 *,
+				struct nfsd3_commitargs *);
+int nfs3svc_encode_voidres(struct svc_rqst *, u32 *, void *);
+int nfs3svc_encode_attrstat(struct svc_rqst *, u32 *,
+				struct nfsd3_attrstat *);
+int nfs3svc_encode_wccstat(struct svc_rqst *, u32 *,
+				struct nfsd3_attrstat *);
+int nfs3svc_encode_diropres(struct svc_rqst *, u32 *,
+				struct nfsd3_diropres *);
+int nfs3svc_encode_accessres(struct svc_rqst *, u32 *,
+				struct nfsd3_accessres *);
 int nfs3svc_encode_readlinkres(struct svc_rqst *, u32 *,
 				struct nfsd3_readlinkres *);
 int nfs3svc_encode_readres(struct svc_rqst *, u32 *, struct nfsd3_readres *);
-int nfs3svc_encode_statfsres(struct svc_rqst *, u32 *,
-				struct nfsd3_statfsres *);
+int nfs3svc_encode_writeres(struct svc_rqst *, u32 *, struct nfsd3_writeres *);
+int nfs3svc_encode_createres(struct svc_rqst *, u32 *,
+				struct nfsd3_diropres *);
+int nfs3svc_encode_renameres(struct svc_rqst *, u32 *,
+				struct nfsd3_renameres *);
+int nfs3svc_encode_linkres(struct svc_rqst *, u32 *,
+				struct nfsd3_linkres *);
 int nfs3svc_encode_readdirres(struct svc_rqst *, u32 *,
 				struct nfsd3_readdirres *);
+int nfs3svc_encode_fsstatres(struct svc_rqst *, u32 *,
+				struct nfsd3_fsstatres *);
+int nfs3svc_encode_fsinfores(struct svc_rqst *, u32 *,
+				struct nfsd3_fsinfores *);
+int nfs3svc_encode_pathconfres(struct svc_rqst *, u32 *,
+				struct nfsd3_pathconfres *);
+int nfs3svc_encode_commitres(struct svc_rqst *, u32 *,
+				struct nfsd3_commitres *);
+
 int nfs3svc_release_fhandle(struct svc_rqst *, u32 *,
-				struct nfsd_fhandle *);
+				struct nfsd3_attrstat *);
 int nfs3svc_release_fhandle2(struct svc_rqst *, u32 *,
-				struct nfsd3_fhandle2 *);
+				struct nfsd3_fhandle_pair *);
 int nfs3svc_encode_entry(struct readdir_cd *, const char *name,
-				int namlen, unsigned long offset, ino_t ino);
+				int namlen, off_t offset, ino_t ino);
+int nfs3svc_encode_entry_plus(struct readdir_cd *, const char *name,
+				int namlen, off_t offset, ino_t ino);
+
+#ifdef __KERNEL__
+
+/*
+ * This is needed in nfs_readdir for encoding NFS3 directory cookies.
+ */
+static inline u32 *
+enc64(u32 *p, u64 val)
+{
+	*p++ = htonl(val >> 32);
+	*p++ = htonl(val & 0xffffffff);
+	return p;
+}
+
+#endif /* __KERNEL__ */
 
-#endif /* LINUX_NFSD_XDR3_H */
+#endif /* _LINUX_NFSD_XDR3_H */
diff -urN linux-2.2.18-pre15/include/linux/nubus.h linux/include/linux/nubus.h
--- linux-2.2.18-pre15/include/linux/nubus.h	Sat Mar 21 11:06:51 1998
+++ linux/include/linux/nubus.h	Mon Dec  3 20:34:13 2001
@@ -1,96 +1,324 @@
+/*
+  nubus.h: various definitions and prototypes for NuBus drivers to use.
 
-struct nubus_slot
-{
-	int slot_flags;
-#define NUBUS_DEVICE_PRESENT	1
-#define NUBUS_DEVICE_ACTIVE	2
-#define NUBUS_DEVICE_IRQ	4
-	__u32 slot_directory;
-	__u32 slot_dlength;
-	__u32 slot_crc;
-	__u8  slot_rev;
-	__u8  slot_format;
-	__u8  slot_lanes;
-	/*
-	 *	Stuff we pulled from the directory
-	 */
-	__u32  slot_dirbase;
-	__u32  slot_thisdir;
-	char   slot_vendor[64];
-	char   slot_cardname[64];
+  Originally written by Alan Cox.
+
+  Hacked to death by C. Scott Ananian and David Huggins-Daines.
+  
+  Some of the constants in here are from the corresponding
+  NetBSD/OpenBSD header file, by Allen Briggs.  We figured out the
+  rest of them on our own. */
+
+#ifndef LINUX_NUBUS_H
+#define LINUX_NUBUS_H
+
+enum nubus_category {
+	NUBUS_CAT_BOARD          = 0x0001,
+	NUBUS_CAT_DISPLAY        = 0x0003,
+	NUBUS_CAT_NETWORK        = 0x0004,
+	NUBUS_CAT_COMMUNICATIONS = 0x0006,
+	NUBUS_CAT_FONT           = 0x0009,
+	NUBUS_CAT_CPU            = 0x000A,
+	/* For lack of a better name */
+	NUBUS_CAT_DUODOCK        = 0x0020
 };
 
-struct nbnamevec 
-{
-	char *name;
-	int id;
+enum nubus_type_network {
+	NUBUS_TYPE_ETHERNET = 0x0001,
+	NUBUS_TYPE_RS232    = 0x0002
+};
+
+enum nubus_type_display {
+	NUBUS_TYPE_VIDEO = 0x0001
+};
+
+enum nubus_type_cpu {
+	NUBUS_TYPE_68020 = 0x0003,
+	NUBUS_TYPE_68030 = 0x0004,
+	NUBUS_TYPE_68040 = 0x0005
+};
+
+/* Known <Cat,Type,SW,HW> tuples: (according to TattleTech and Slots)
+ *  68030 motherboards: <10,4,0,24>
+ *  68040 motherboards: <10,5,0,24>
+ *  DuoDock Plus: <32,1,1,2>
+ *
+ *  Toby Frame Buffer card: <3,1,1,1>
+ *  RBV built-in video (IIci): <3,1,1,24>
+ *  Valkyrie built-in video (Q630): <3,1,1,46>
+ *  Macintosh Display Card: <3,1,1,25>
+ *  Sonora built-in video (P460): <3,1,1,34>
+ *  Jet framebuffer (DuoDock Plus): <3,1,1,41>
+ *
+ *  SONIC comm-slot/on-board and DuoDock Ethernet: <4,1,1,272>
+ *  SONIC LC-PDS Ethernet (Dayna, but like Apple 16-bit, sort of): <4,1,1,271>
+ *  Apple SONIC LC-PDS Ethernet ("Apple Ethernet LC Twisted-Pair Card"): <4,1,0,281>
+ *  Sonic Systems Ethernet A-Series Card: <4,1,268,256>
+ *  Asante MacCon NuBus-A: <4,1,260,256> (alpha-1.0,1.1 revision)
+ *   ROM on the above card: <2,1,0,0>
+ *  Cabletron ethernet card: <4,1,1,265>
+ *  Farallon ethernet card: <4,1,268,256> (identical to Sonic Systems card)
+ *  Kinetics EtherPort IIN: <4,1,259,262>
+ *  API Engineering EtherRun_LCa PDS enet card: <4,1,282,256>
+ *
+ *  Add your devices to the list!  You can obtain the "Slots" utility
+ *  from Apple's FTP site at:
+ *  ftp://dev.apple.com/devworld/Tool_Chest/Devices_-_Hardware/NuBus_Slot_Manager/
+ *
+ *  Alternately, TattleTech can be found at any Info-Mac mirror site.  
+ *  or from its distribution site: ftp://ftp.decismkr.com/dms
+ */
+
+/* DrSW: Uniquely identifies the software interface to a board.  This
+   is usually the one you want to look at when writing a driver.  It's
+   not as useful as you think, though, because as we should know by
+   now (duh), "Apple Compatible" can mean a lot of things... */
+
+/* Add known DrSW values here */
+enum nubus_drsw {
+	/* NUBUS_CAT_DISPLAY */
+	NUBUS_DRSW_APPLE       = 0x0001,
+	NUBUS_DRSW_APPLE_HIRES = 0x0013, /* MacII HiRes card driver */
+	
+	/* NUBUS_CAT_NETWORK */
+	NUBUS_DRSW_CABLETRON        = 0x0001,
+	NUBUS_DRSW_SONIC_LC         = 0x0001,
+	NUBUS_DRSW_KINETICS         = 0x0103,
+	NUBUS_DRSW_ASANTE           = 0x0104,
+	NUBUS_DRSW_DAYNA            = 0x010b,
+	NUBUS_DRSW_FARALLON         = 0x010c,
+	NUBUS_DRSW_APPLE_SN         = 0x010f,
+	NUBUS_DRSW_FOCUS            = 0x011a,
+	NUBUS_DRSW_ASANTE_CS        = 0x011d, /* use asante SMC9194 driver */
+
+	/* NUBUS_CAT_CPU */
+	NUBUS_DRSW_NONE        = 0x0000,
+};
+
+/* DrHW: Uniquely identifies the hardware interface to a board (or at
+   least, it should...  some video cards are known to incorrectly
+   identify themselves as Toby cards) */
+
+/* Add known DrHW values here */
+enum nubus_drhw {
+	/* NUBUS_CAT_DISPLAY */
+	NUBUS_DRHW_APPLE_TFB	= 0x0001, /* Toby frame buffer card */
+	NUBUS_DRHW_APPLE_RBV1   = 0x0018, /* IIci RBV video */
+	NUBUS_DRHW_APPLE_MDC    = 0x0019, /* Macintosh Display Card */
+	NUBUS_DRHW_APPLE_SONORA = 0x0022, /* Sonora built-in video */
+	NUBUS_DRHW_APPLE_VALKYRIE = 0x002e,
+	NUBUS_DRHW_APPLE_JET =    0x0029, /* Jet framebuffer (DuoDock) */
+	
+	/* NUBUS_CAT_NETWORK */
+	NUBUS_DRHW_INTERLAN     = 0x0100,
+	NUBUS_DRHW_SMC9194      = 0x0101,
+	NUBUS_DRHW_KINETICS     = 0x0106,
+	NUBUS_DRHW_CABLETRON    = 0x0109,
+	NUBUS_DRHW_ASANTE_LC    = 0x010f,
+	NUBUS_DRHW_SONIC        = 0x0110,
+	NUBUS_DRHW_APPLE_SONIC_LC = 0x0119,
+};
+
+/* Resource IDs: These are the identifiers for the various weird and
+   wonderful tidbits of information that may or may not reside in the
+   NuBus ROM directory. */
+enum nubus_res_id {
+	NUBUS_RESID_TYPE         = 0x0001,
+	NUBUS_RESID_NAME         = 0x0002,
+	NUBUS_RESID_ICON         = 0x0003,
+	NUBUS_RESID_DRVRDIR      = 0x0004,
+	NUBUS_RESID_LOADREC      = 0x0005,
+	NUBUS_RESID_BOOTREC      = 0x0006,
+	NUBUS_RESID_FLAGS        = 0x0007,
+	NUBUS_RESID_HWDEVID      = 0x0008,
+	NUBUS_RESID_MINOR_BASEOS = 0x000a,
+	NUBUS_RESID_MINOR_LENGTH = 0x000b,
+	NUBUS_RESID_MAJOR_BASEOS = 0x000c,
+	NUBUS_RESID_MAJOR_LENGTH = 0x000d,
+	NUBUS_RESID_CICN         = 0x000f,
+	NUBUS_RESID_ICL8         = 0x0010,
+	NUBUS_RESID_ICL4         = 0x0011,
+};
+
+/* Category-specific resources. */
+enum nubus_board_res_id {
+	NUBUS_RESID_BOARDID	 = 0x0020,
+	NUBUS_RESID_PRAMINITDATA = 0x0021,
+	NUBUS_RESID_PRIMARYINIT	 = 0x0022,
+	NUBUS_RESID_TIMEOUTCONST = 0x0023,
+	NUBUS_RESID_VENDORINFO	 = 0x0024,
+	NUBUS_RESID_BOARDFLAGS	 = 0x0025,
+	NUBUS_RESID_SECONDINIT	 = 0x0026,
+
+	/* Not sure why Apple put these next two in here */
+	NUBUS_RESID_VIDNAMES = 0x0041,
+	NUBUS_RESID_VIDMODES = 0x007e
+};
+
+/* Fields within the vendor info directory */
+enum nubus_vendor_res_id {
+	NUBUS_RESID_VEND_ID     = 0x0001,
+	NUBUS_RESID_VEND_SERIAL = 0x0002,
+	NUBUS_RESID_VEND_REV    = 0x0003,
+	NUBUS_RESID_VEND_PART   = 0x0004,
+	NUBUS_RESID_VEND_DATE   = 0x0005
+};
+
+enum nubus_net_res_id {
+	NUBUS_RESID_MAC_ADDRESS  = 0x0080
+};
+
+enum nubus_cpu_res_id {
+	NUBUS_RESID_MEMINFO      = 0x0081,
+	NUBUS_RESID_ROMINFO      = 0x0082
+};
+
+enum nubus_display_res_id {
+	NUBUS_RESID_GAMMADIR	= 0x0040,
+	NUBUS_RESID_FIRSTMODE	= 0x0080,
+	NUBUS_RESID_SECONDMODE	= 0x0081,
+	NUBUS_RESID_THIRDMODE	= 0x0082,
+	NUBUS_RESID_FOURTHMODE	= 0x0083,
+	NUBUS_RESID_FIFTHMODE	= 0x0084,
+	NUBUS_RESID_SIXTHMODE	= 0x0085
 };
 
 struct nubus_dir
 {
 	unsigned char *base;
-	int length;
-	int count;
+	unsigned char *ptr;
+	int done;
 	int mask;
 };
 
 struct nubus_dirent
 {
+	unsigned char *base;
 	unsigned char type;
-	int value;	/* Actually 24bits used */
+	__u32 data;	/* Actually 24bits used */
 	int mask;
-	int base;	/* For dirptr function */
 };
 
-struct nubus_type
-{
-	__u16 category;
-	__u16 type;
-	__u16 DrHW;
-	__u16 DrSW;
-};
-
-#define NUBUS_CAT_BOARD			0x0001
-#define NUBUS_CAT_DISPLAY		0x0003
-#define NUBUS_CAT_NETWORK		0x0004
-#define NUBUS_CAT_COMMUNICATIONS	0x0006
-#define NUBUS_CAT_FONT			0x0009
-#define NUBUS_CAT_CPU			0x000A
-
-#define RES_ID_TYPE		0x0001
-#define RES_ID_NAME		0x0002
-#define RES_ID_BOARD_DIR	0x0001
-#define RES_ID_FLAGS		0x0007
-
-struct nubus_device_specifier
-{
-	int (*setup)(struct nubus_device_specifier *, int slot, struct nubus_type *);
-	struct nubus_device_specifier *next;
+struct nubus_board {
+	struct nubus_board* next;
+	struct nubus_dev* first_dev;
+	
+        /* Only 9-E actually exist, though 0-8 are also theoretically
+	   possible, and 0 is a special case which represents the
+	   motherboard and onboard peripherals (Ethernet, video) */
+	int slot;
+	/* For slot 0, this is bogus. */
+	char name[64];
+
+	/* Format block */
+	unsigned char* fblock;
+	/* Root directory (does *not* always equal fblock + doffset!) */
+	unsigned char* directory;
+	
+	unsigned long slot_addr;
+	/* Offset to root directory (sometimes) */
+	unsigned long doffset;
+	/* Length over which to compute the crc */
+	unsigned long rom_length;
+	/* Completely useless most of the time */
+	unsigned long crc;
+	unsigned char rev;
+	unsigned char format;
+	unsigned char lanes;
 };
 
+struct nubus_dev {
+	/* Next link in device list */
+	struct nubus_dev* next;
+	/* Directory entry in /proc/bus/nubus */
+	struct proc_dir_entry* procdir;
+
+	/* The functional resource ID of this device */
+	unsigned char resid;
+	/* These are mostly here for convenience; we could always read
+	   them from the ROMs if we wanted to */
+	unsigned short category;
+	unsigned short type;
+	unsigned short dr_sw;
+	unsigned short dr_hw;
+	/* This is the device's name rather than the board's.
+	   Sometimes they are different.  Usually the board name is
+	   more correct. */
+	char name[64];
+	/* MacOS driver (I kid you not) */
+	unsigned char* driver;
+	/* Actually this is an offset */
+	unsigned long iobase;
+	unsigned long iosize;
+	unsigned char flags, hwdevid;
+	
+	/* Functional directory */
+	unsigned char* directory;
+	/* Much of our info comes from here */
+	struct nubus_board* board;
+};
 
-extern void register_nubus_device(struct nubus_device_specifier *nb);
-extern void unregister_nubus_device(struct nubus_device_specifier *nb);
-
-extern struct nubus_dir *nubus_openrootdir(int slot);
-extern struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d);
-extern void nubus_closedir(struct nubus_dir *);
-extern struct nubus_dirent *nubus_readdir(struct nubus_dir *);
-extern unsigned char *nubus_dirptr(struct nubus_dirent *d);
-extern void nubus_strncpy(int slot, void *to, unsigned char *p, int len);
-extern void nubus_memcpy(int slot, void *to, unsigned char *p, int len);
-extern void nubus_init(void);
-extern void nubus_sweep_video(void);
-extern int nubus_ethernet_addr(int slot, unsigned char *addr);
+/* This is all NuBus devices (used to find devices later on) */
+extern struct nubus_dev* nubus_devices;
+/* This is all NuBus cards */
+extern struct nubus_board* nubus_boards;
+
+/* Generic NuBus interface functions, modelled after the PCI interface */
+void nubus_init(void);
+void nubus_scan_bus(void);
+void nubus_proc_init(void);
+int get_nubus_list(char *buf);
+int nubus_proc_attach_device(struct nubus_dev *dev);
+int nubus_proc_detach_device(struct nubus_dev *dev);
+/* If we need more precision we can add some more of these */
+struct nubus_dev* nubus_find_device(unsigned short category,
+				    unsigned short type,
+				    unsigned short dr_hw,
+				    unsigned short dr_sw,
+				    const struct nubus_dev* from);
+struct nubus_dev* nubus_find_type(unsigned short category,
+				  unsigned short type,
+				  const struct nubus_dev* from);
+/* Might have more than one device in a slot, you know... */
+struct nubus_dev* nubus_find_slot(unsigned int slot,
+				  const struct nubus_dev* from);
+
+/* These are somewhat more NuBus-specific.  They all return 0 for
+   success and -1 for failure, as you'd expect. */
+
+/* The root directory which contains the board and functional
+   directories */
+int nubus_get_root_dir(const struct nubus_board* board,
+		       struct nubus_dir* dir);
+/* The board directory */
+int nubus_get_board_dir(const struct nubus_board* board,
+			struct nubus_dir* dir);
+/* The functional directory */
+int nubus_get_func_dir(const struct nubus_dev* dev,
+		       struct nubus_dir* dir);
+
+/* These work on any directory gotten via the above */
+int nubus_readdir(struct nubus_dir* dir,
+		  struct nubus_dirent* ent);
+int nubus_find_rsrc(struct nubus_dir* dir,
+		    unsigned char rsrc_type,
+		    struct nubus_dirent* ent);
+int nubus_rewinddir(struct nubus_dir* dir);
+
+/* Things to do with directory entries */
+int nubus_get_subdir(const struct nubus_dirent* ent,
+		     struct nubus_dir* dir);
+void nubus_get_rsrc_mem(void* dest,
+			const struct nubus_dirent *dirent,
+			int len);
+void nubus_get_rsrc_str(void* dest,
+			const struct nubus_dirent *dirent,
+			int maxlen);
 
-extern __inline void *nubus_slot_addr(int slot)
+/* We'd like to get rid of this eventually.  Only daynaport.c uses it now. */
+extern inline void *nubus_slot_addr(int slot)
 {
 	return (void *)(0xF0000000|(slot<<24));
 }
 
-extern int nubus_hwreg_present(volatile void *ptr);
-
-extern void nubus_init_via(void);
-extern int nubus_free_irq(int slot);
-extern int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *));
-
+#endif LINUX_NUBUS_H
diff -urN linux-2.2.18-pre15/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h
--- linux-2.2.18-pre15/include/linux/sunrpc/auth.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/sunrpc/auth.h	Mon Dec  3 20:34:13 2001
@@ -38,6 +38,7 @@
  * Client authentication handle
  */
 #define RPC_CREDCACHE_NR	8
+#define RPC_CREDCACHE_MASK	(RPC_CREDCACHE_NR - 1)
 struct rpc_auth {
 	struct rpc_cred *	au_credcache[RPC_CREDCACHE_NR];
 	unsigned long		au_expire;	/* cache expiry interval */
diff -urN linux-2.2.18-pre15/include/linux/sysctl.h linux/include/linux/sysctl.h
--- linux-2.2.18-pre15/include/linux/sysctl.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/sysctl.h	Mon Dec  3 20:34:13 2001
@@ -106,6 +106,7 @@
 	KERN_SYSRQ=38,		/* int: Sysreq enable */
 	KERN_SHMALL=41,		/* int: maximum size of shared memory */
 	KERN_SPARC_STOP_A=44,	/* int: Sparc Stop-A enable */
+	KERN_HOTPLUG=49,	/* string: path to hotplug policy agent */
 };
 
 
diff -urN linux-2.2.18-pre15/include/linux/usb.h linux/include/linux/usb.h
--- linux-2.2.18-pre15/include/linux/usb.h	Mon Dec  3 20:33:41 2001
+++ linux/include/linux/usb.h	Mon Dec  3 20:34:13 2001
@@ -262,7 +262,7 @@
 
   	struct usb_endpoint_descriptor *endpoint;
 
-	unsigned char *extra;
+	unsigned char *extra;	/* Extra descriptors */
 	int extralen;
 } __attribute__ ((packed));
 
@@ -289,6 +289,9 @@
 	__u8  MaxPower;
 
 	struct usb_interface *interface;
+
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
 } __attribute__ ((packed));
 
 /* String descriptor */
diff -urN linux-2.2.18-pre15/include/linux/xpds-ioctl.h linux/include/linux/xpds-ioctl.h
--- linux-2.2.18-pre15/include/linux/xpds-ioctl.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/xpds-ioctl.h	Mon Dec  3 20:34:13 2001
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999, 2000 Xpeed, Inc.
+ * xpds-ioctl.h $Revision: 1.6 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ */
+#ifndef XPDS_IOCTL_H
+#define XPDS_IOCTL_H 1
+
+#include <linux/types.h>
+#include <linux/sockios.h>
+
+#define XPDS_IOCTL_SET_DLCI		(SIOCDEVPRIVATE + 0)
+#define XPDS_IOCTL_SET_DEBUG		(SIOCDEVPRIVATE + 1)
+#define XPDS_IOCTL_SET_LT		(SIOCDEVPRIVATE + 2)
+#define XPDS_IOCTL_SET_IDSL_MODE	(SIOCDEVPRIVATE + 3)
+#define XPDS_IOCTL_SET_MODE		XPDS_IOCTL_SET_IDSL_MODE
+#define XPDS_IOCTL_SET_SDSL_SPEED	(SIOCDEVPRIVATE + 4)
+#define XPDS_IOCTL_SET_SDSL_INVERT	(SIOCDEVPRIVATE + 5)
+#define XPDS_IOCTL_SET_SDSL_SWAP	(SIOCDEVPRIVATE + 6)
+#define XPDS_IOCTL_INSTALL_FLASH	(SIOCDEVPRIVATE + 7)
+#define XPDS_IOCTL_GET_SDSL_INFO	(SIOCDEVPRIVATE + 8)
+#define XPDS_IOCTL_SET_SDSL_INFO	(SIOCDEVPRIVATE + 9)
+#define XPDS_IOCTL_GET_SDSL_STATE	(SIOCDEVPRIVATE + 10)
+#define XPDS_IOCTL_SET_LOOPBACK		(SIOCDEVPRIVATE + 11)
+#define XPDS_IOCTL_SET_SDSL_LOOPBACK	XPDS_IOCTL_SET_LOOPBACK
+#define XPDS_IOCTL_SET_ASIC_LOOPBACK	XPDS_IOCTL_SET_LOOPBACK
+#define XPDS_IOCTL_SET_BRIDGED_ETHERNET	(SIOCDEVPRIVATE + 13)
+#define XPDS_IOCTL_SET_DLCI_CR		(SIOCDEVPRIVATE + 14)
+#define XPDS_IOCTL_SET_DLCI_LMI		(SIOCDEVPRIVATE + 15)
+
+/*
+ * For XPDS_IOCTL_GET_SDSL_INFO and XPDS_IOCTL_SET_SDSL_INFO.
+ */
+typedef struct {
+	__u16			seprom_revision;
+	__u8			hardware_version[2];
+	__u8			software_version[2];
+	__u8			firmware_version[2];
+	__u8			mfg_date[4];
+	__u8			mac_address[6];
+	__u8			serial_number[16];
+} xpds_serial_data_t;
+
+/*
+ * For XPDS_IOCTL_SET_LOOPBACK
+ */
+typedef struct {
+	int	loopback_type;
+	int	on;
+} xpds_loopback_parameters_t;
+
+#define XPDS_ASIC_LOOPBACK	1
+#define XPDS_SDSL_LOOPBACK	2
+
+#endif
diff -urN linux-2.2.18-pre15/init/main.c linux/init/main.c
--- linux-2.2.18-pre15/init/main.c	Mon Dec  3 20:33:41 2001
+++ linux/init/main.c	Mon Dec  3 20:34:13 2001
@@ -1289,6 +1289,10 @@
 			console_loglevel = 10;
 			continue;
 		}
+		if (!strcmp(line,"quiet")) {
+			console_loglevel = 4;
+			continue;
+		}
 		if (!strncmp(line,"init=",5)) {
 			line += 5;
 			execute_command = line;
diff -urN linux-2.2.18-pre15/kernel/kmod.c linux/kernel/kmod.c
--- linux-2.2.18-pre15/kernel/kmod.c	Wed Jun  7 14:26:44 2000
+++ linux/kernel/kmod.c	Mon Dec  3 20:34:13 2001
@@ -185,3 +185,26 @@
 	}
 	return 0;
 }
+
+
+#ifdef CONFIG_HOTPLUG
+/*
+	hotplug path is set via /proc/sys
+	invoked by hotplug-aware bus drivers,
+	with exec_usermodehelper and some thread-spawner
+
+	argv [0] = hotplug_path;
+	argv [1] = "usb", "scsi", "pci", "network", etc;
+	... plus optional type-specific parameters
+	argv [n] = 0;
+
+	envp [*] = HOME, PATH; optional type-specific parameters
+
+	a hotplug bus should invoke this for device add/remove
+	events.  the command is expected to load drivers when
+	necessary, and may perform additional system setup.
+*/
+char hotplug_path[256] = "/sbin/hotplug";
+
+#endif
+
diff -urN linux-2.2.18-pre15/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-2.2.18-pre15/kernel/ksyms.c	Mon Dec  3 20:33:41 2001
+++ linux/kernel/ksyms.c	Mon Dec  3 20:34:13 2001
@@ -74,6 +74,9 @@
 #ifdef CONFIG_KMOD
 EXPORT_SYMBOL(request_module);
 EXPORT_SYMBOL(exec_usermodehelper);
+#ifdef CONFIG_HOTPLUG
+EXPORT_SYMBOL(hotplug_path);
+#endif
 #endif
 
 #ifdef CONFIG_MODULES
diff -urN linux-2.2.18-pre15/kernel/sched.c linux/kernel/sched.c
--- linux-2.2.18-pre15/kernel/sched.c	Mon Dec  3 20:33:41 2001
+++ linux/kernel/sched.c	Mon Dec  3 20:34:13 2001
@@ -1931,13 +1931,21 @@
 	if (t.tv_sec == 0 && t.tv_nsec <= 2000000L &&
 	    current->policy != SCHED_OTHER)
 	{
+		unsigned long delay;
+		
 		/*
 		 * Short delay requests up to 2 ms will be handled with
 		 * high precision by a busy wait for all real-time processes.
 		 *
 		 * Its important on SMP not to do this holding locks.
 		 */
-		udelay((t.tv_nsec + 999) / 1000);
+		 
+		delay=(t.tv_nsec + 999) / 1000;
+		
+		if(delay>10000)
+			mdelay(delay);
+		else
+			udelay(delay);
 		return 0;
 	}
 
diff -urN linux-2.2.18-pre15/kernel/sysctl.c linux/kernel/sysctl.c
--- linux-2.2.18-pre15/kernel/sysctl.c	Wed Jun  7 14:26:44 2000
+++ linux/kernel/sysctl.c	Mon Dec  3 20:34:13 2001
@@ -41,6 +41,9 @@
 
 #ifdef CONFIG_KMOD
 extern char modprobe_path[];
+#ifdef CONFIG_HOTPLUG
+extern char hotplug_path[];
+#endif
 #endif
 #ifdef CONFIG_CHR_DEV_SG
 extern int sg_big_buff;
@@ -207,6 +210,10 @@
 #ifdef CONFIG_KMOD
 	{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
 	 0644, NULL, &proc_dostring, &sysctl_string },
+#ifdef CONFIG_HOTPLUG
+	{KERN_HOTPLUG, "hotplug", &hotplug_path, 256,
+	 0644, NULL, &proc_dostring, &sysctl_string },
+#endif
 #endif
 #ifdef CONFIG_CHR_DEV_SG
 	{KERN_SG_BIG_BUFF, "sg-big-buff", &sg_big_buff, sizeof (int),
diff -urN linux-2.2.18-pre15/net/core/dev.c linux/net/core/dev.c
--- linux-2.2.18-pre15/net/core/dev.c	Mon Dec  3 20:33:41 2001
+++ linux/net/core/dev.c	Mon Dec  3 20:34:13 2001
@@ -1914,6 +1914,7 @@
 extern void dlci_setup(void);
 extern int dmascc_init(void);
 extern int sm_init(void);
+extern void xpdsl_init(void);
 
 extern int baycom_ser_fdx_init(void);
 extern int baycom_ser_hdx_init(void);
@@ -2097,6 +2098,9 @@
 #endif
 #ifdef CONFIG_WANXL
 	wanxl_init();
+#endif
+#ifdef CONFIG_XPEED
+	xpdsl_init();
 #endif
 #ifdef CONFIG_PC300
 	cpc_init();
diff -urN linux-2.2.18-pre15/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- linux-2.2.18-pre15/net/sunrpc/auth.c	Mon Dec  3 20:33:41 2001
+++ linux/net/sunrpc/auth.c	Mon Dec  3 20:34:13 2001
@@ -147,7 +147,7 @@
 {
 	int		nr;
 
-	nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+	nr = (cred->cr_uid & RPC_CREDCACHE_MASK);
 	cred->cr_next = auth->au_credcache[nr];
 	auth->au_credcache[nr] = cred;
 	cred->cr_count++;
@@ -164,7 +164,7 @@
 	int		nr = 0;
 
 	if (!(taskflags & RPC_TASK_ROOTCREDS))
-		nr = current->uid % RPC_CREDCACHE_NR;
+		nr = current->uid & RPC_CREDCACHE_MASK;
 
 	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
@@ -197,7 +197,7 @@
 	struct rpc_cred	**q, *cr;
 	int		nr;
 
-	nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+	nr = (cred->cr_uid & RPC_CREDCACHE_MASK);
 	q = &auth->au_credcache[nr];
 	while ((cr = *q) != NULL) {
 		if (cred == cr) {
diff -urN linux-2.2.18-pre15/net/sunrpc/auth_null.c linux/net/sunrpc/auth_null.c
--- linux-2.2.18-pre15/net/sunrpc/auth_null.c	Mon Dec  3 20:33:41 2001
+++ linux/net/sunrpc/auth_null.c	Mon Dec  3 20:34:13 2001
@@ -54,6 +54,7 @@
 		return NULL;
 	cred->cr_count = 0;
 	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
+	cred->cr_uid = current->uid;
 
 	return cred;
 }
diff -urN linux-2.2.18-pre15/net/sunrpc/svc.c linux/net/sunrpc/svc.c
--- linux-2.2.18-pre15/net/sunrpc/svc.c	Tue Jan  4 10:12:27 2000
+++ linux/net/sunrpc/svc.c	Mon Dec  3 20:34:13 2001
@@ -32,6 +32,9 @@
 	struct svc_serv	*serv;
 
 	xdr_init();
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
 
 	if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
 		return NULL;
@@ -267,8 +270,8 @@
 	if (prog != progp->pg_prog)
 		goto err_bad_prog;
 
-	versp = progp->pg_vers[vers];
-	if (!versp || vers >= progp->pg_nvers)
+	if (vers >= progp->pg_nvers ||
+	  !(versp = progp->pg_vers[vers]))
 		goto err_bad_vers;
 
 	procp = versp->vs_proc + proc;
diff -urN linux-2.2.18-pre15/scripts/ksymoops/ksymoops.c linux/scripts/ksymoops/ksymoops.c
--- linux-2.2.18-pre15/scripts/ksymoops/ksymoops.c	Mon Aug  9 12:04:58 1999
+++ linux/scripts/ksymoops/ksymoops.c	Mon Dec  3 20:34:13 2001
@@ -483,7 +483,7 @@
 	multi_opt(spec_m, spec_M, 'm', *system_map);
 
 	printf("WARNING: This version of ksymoops is obsolete.\n");
-	printf("WARNING: The current version can be obtained from ftp://ftp.ocs.com.au/pub/ksymoops\n");
+	printf("WARNING: The current version can be obtained from ftp://ftp.<country>.kernel.org/pub/linux/utils/kernel/ksymoops\n");
 	warnings += 2;
 
 	printf("Options used:");
